Skip to main content

Marona Python SDK

Run Agents through one managed API, with structured output, local tools, Hub capabilities, streaming, realtime and A2A collaboration.

Install

Version 2.6.0. Package registry.

Python 3.10+.

python -m pip install --upgrade marona==2.6.0

Set MARONA_API_KEY in your backend environment. Never commit it, log it, or embed a developer key in browser or mobile code. Use an exact accessible marona/* model alias from Marona Platform.

Agent quickstart

Save as examples/agent_example.py. Run without arguments for text, or with a real PNG/JPEG path for document extraction. Use stable application user IDs and a distinct session ID for each conversation.

import asyncio
import base64
import os
import sys
from pathlib import Path

from marona import Agent, Marona, Runner


async def main():
    async with Marona(
        api_key=os.environ["MARONA_API_KEY"],
        base_url=os.getenv("MARONA_RUNTIME_URL", "https://edge.marona.ai"),
    ) as marona:
        agent = Agent(
            name="Document Assistant",
            model=os.getenv("MARONA_MODEL", "marona/qwen-qwen3.8-max-0902"),
            instructions="Answer clearly and concisely.",
            tool_choice="none",
        )
        user_input = "Reply with exactly OK."
        if len(sys.argv) > 1:
            path = Path(sys.argv[1])
            mime = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg"}[path.suffix.lower()]
            data_url = f"data:{mime};base64," + base64.b64encode(path.read_bytes()).decode("ascii")
            user_input = [{"role": "user", "content": [
                {"type": "input_text", "text": "Describe this image and transcribe any visible text."},
                {"type": "input_image", "image_url": data_url},
            ]}]
        result = await Runner.run(
            agent, user_input, user_id="example-user", session_id="example-session"
        )
        print(result.output)


if __name__ == "__main__":
    asyncio.run(main())
python examples/agent_example.py
python examples/agent_example.py image.png

Encode actual file bytes with standard Base64. Image and file blocks accept raw Base64 in image_data / file_data, or complete matching data URLs. Do not encode a path, encode twice, or send placeholder data. Remote URLs must be reachable by the runtime. PDF, audio and realtime support depend on the selected model; render PDF pages to images when native PDF is unavailable.

Structured output through Agent

The following fragment uses the authenticated client from the quickstart. document_input is your text or document message input.

from pydantic import BaseModel, ConfigDict
from marona import Agent, Runner

class Extraction(BaseModel):
    model_config = ConfigDict(extra="forbid")
    text: str

agent = Agent(
    name="Document Reader",
    model="marona/qwen-qwen3.8-max-0902",
    instructions="Extract the document text.",
    output_type=Extraction,
    output_schema_strict=True,
)
result = await Runner.run(agent, document_input, user_id="user-123")
print(result.output.text)

The explicit strict option asks the provider to enforce the supplied schema; the SDK also validates the final result before returning it. Strict mode requires a compatible model and schema. Declare every object property as required, use nullable values for optional fields, and set additionalProperties to false. Unsupported model capabilities or invalid output produce errors; they do not silently fall back to unstructured text. Input schemas, function tool schemas, guardrails, dynamic instructions and lifecycle hooks are also supported.

Apps and user connections

page = await marona.apps.list_async(user_id="user-123", limit=20)
action = await marona.apps.connect_async("calendar", user_id="user-123")
# Complete any returned authorization_url before calling protected tools.
await marona.apps.disconnect_async("calendar")

Listing an App does not authorize its protected operations. Follow the returned connection action for the end user. Use hub.connect to expose a scoped set of App or Skill capabilities to an Agent; Apps authorization and Hub tool discovery are separate operations.

A2A collaboration and serving

import os
from marona import A2APeer, A2AServer, Agent

peer = A2APeer(name="reviewer", url="https://reviewer.example.com")
coordinator = Agent(name="Coordinator", peers=[peer])
server = A2AServer(
    agent=coordinator,
    url="https://coordinator.example.com",
    skills=[{"id": "review", "name": "Review", "description": "Review a document"}],
)
# Apply production authentication and TLS at your service boundary.
server.run(host="127.0.0.1", port=8100)

Peers support Agent Card discovery, REST/JSON-RPC messages, task retrieval, cancellation, continuation and streamed task events. Servers persist task state. Use explicit credentials, restricted skills, HTTPS, and an appropriate durable task directory for production. Local JSON stores are for a single service instance; multi-instance deployments require coordinated task storage.

Push notifications are not implemented.

Brain2Agent (B2A)

Native B2A 1.0 uses one multimodal message contract in both directions. The independent neural service owns signal decoding and feedback encoding, with vendor-hosted MCP, HTTP or local plugin backends. Models and device drivers are supplied by the interface provider. Configure Agent.protocols and connect with Runner.connect, following the persistent realtime session lifecycle. These examples read one captured signal file using your configured provider.

One configured protocol is selected automatically. With several configured protocols, select by name (protocol="b2a" in Python, the corresponding option/overload in other SDKs). Duplicate names and ambiguous selection are rejected. Implement AgentProtocol to add another protocol without adding Agent properties. Existing A2A peers and servers retain their agent-to-agent role; they are not automatically converted into input protocols. Agent.b2a and B2ARunner remain compatibility APIs.

Save as examples/b2a_message.py:

"""Run a captured signal through an independent provider and an Agent-bound B2A session."""

import asyncio
import json
import os
import sys
from pathlib import Path
from marona import Agent, B2A, B2ACodec, Runner, Marona

INSTRUCTIONS = """Interpret the decoded messages using its context.
Preserve uncertainty and ask for clarification when needed.
Use connected tools under Marona's permissions and confirmation requirements.
"""


async def main():
    signal = json.loads(Path(sys.argv[1]).read_text())
    neural_service = B2ACodec(
        os.environ["B2A_CODEC_URL"],
        token=os.environ["B2A_NEURAL_TOKEN"],
        transport=os.environ.get("B2A_CODEC_TRANSPORT", "mcp"),
    )
    async with Marona() as client:
        agent = Agent(
            name="B2A Message Agent",
            model=os.environ.get("MARONA_MODEL", "marona/auto"),
            instructions=INSTRUCTIONS,
            tools=client.hub.connect(),
            protocols=[B2A(
                provider=neural_service, output_profile=os.environ["B2A_OUTPUT_PROFILE"]
            )],
        )
        async with await Runner.connect(
            agent,
            user_id=os.environ["B2A_USER_ID"],
            session_id=os.environ["B2A_SESSION_ID"],
        ) as session:
            await session.send_signal(signal)
            async for event in session:
                print(event)
                if event.type in {"feedback", "signal.status", "error"}:
                    break  # This file sends one captured window; keep open for continuous input.


if __name__ == "__main__":
    asyncio.run(main())

Single run with structured output

Save as examples/b2a_structured.py. This opens a connection, decodes the signal, runs the agent, validates the response, encodes feedback, and closes the connection.

"""Run a captured signal through an independent provider and an Agent-bound B2A session."""

import asyncio
import json
import os
import sys
from pathlib import Path
from pydantic import BaseModel, ConfigDict

from marona import Agent, B2A, B2ACodec, Runner, Marona

class NeuralResponse(BaseModel):
    model_config = ConfigDict(extra="forbid")
    text: str


INSTRUCTIONS = """Interpret the decoded messages using its context.
Preserve uncertainty and ask for clarification when needed.
Use connected tools under Marona's permissions and confirmation requirements.
"""


async def main():
    signal = json.loads(Path(sys.argv[1]).read_text())
    neural_service = B2ACodec(
        os.environ["B2A_CODEC_URL"],
        token=os.environ["B2A_NEURAL_TOKEN"],
        transport=os.environ.get("B2A_CODEC_TRANSPORT", "mcp"),
    )
    async with Marona() as client:
        agent = Agent(
            name="B2A Message Agent",
            model=os.environ.get("MARONA_MODEL", "marona/qwen-qwen3.8-max-0902"),
            instructions=INSTRUCTIONS,
            output_type=NeuralResponse,
            output_schema_strict=True,
            tools=client.hub.connect(),
            protocols=[B2A(
                provider=neural_service, output_profile=os.environ["B2A_OUTPUT_PROFILE"]
            )],
        )
        result = await Runner.run(
            agent, signal,
            user_id=os.environ["B2A_USER_ID"],
            session_id=os.environ["B2A_SESSION_ID"],
        )
        print(result.output.text)



if __name__ == "__main__":
    asyncio.run(main())

Python returns the declared Pydantic model in result.output. Other SDKs validate the declared JSON schema and expose their language's structured output representation. Invalid output is rejected before encoding. Decoder abstention, required approval, or failure does not become a successful typed result; use a persistent connection for interactive clarification, approvals, and feedback delivery acknowledgments.

The SDK manages protocol messages, sequencing, event cursors, decoding and encoding. Set B2A_CODEC_URL, B2A_NEURAL_TOKEN, B2A_OUTPUT_PROFILE, B2A_USER_ID and B2A_SESSION_ID. Use B2A_CODEC_TRANSPORT=mcp or http. session_id identifies an application conversation; each connection gets a separate native transport session. The application authorizes processing before connecting; device and protocol scope fields are supplied internally. Closing revokes those scopes.

Keep the session open to send more signals. Feedback events contain encoded output; printing them does not deliver feedback. An interface driver acknowledges presentation only after delivery. Failed or uncertain sends are never replayed automatically. Native B2A supports server-hosted Hub tools and agent graphs; SDK-local functions, callbacks and peer callbacks require server-hosted equivalents. B2A connections automatically negotiate HTTP SSE with current runtimes. Sending a signal returns when accepted. message events carry the standard envelope during generation and at completion; agent.delta remains a text-preview compatibility event. Validated, encoded output arrives as feedback. Keep consuming the session stream for subsequent turns. The SDK handles transport and sequencing; no background task or polling loop is required in application code. Heartbeats keep an active stream alive without imposing a generation-wide timeout. Runner.run collects the completed result and retains its configured run limit. New connections require a B2A 1.0 runtime; the connection's streaming flag reports whether HTTP SSE is available.

One message format in both directions

{
  "protocol": "b2a",
  "version": "1.0",
  "type": "message",
  "source": "neural_decoder",
  "message_id": "message-123",
  "timestamp": "2026-11-12T08:00:00Z",
  "payload": {"content": [
    {"type": "text", "data": "Explain this observation"},
    {"type": "audio", "mime_type": "audio/wav", "uri": "b2a://session-123/audio-001"}
  ]}
}

Brain → Agent and Agent → Brain use this same schema; source identifies the sender (brain, neural_decoder, agent, or a provider-defined source). The SDK binds the session ID, sequence and context revision. Source labels do not grant authority. payload.content supports text, audio, image, video, control, features and namespaced extensions. Binary inline data uses encoding: "base64" and a matching mime_type; numerical values stay structured. Multiple representations of one utterance can share representation_id and timing in milliseconds.

Providers return status, message, provenance and uncertainty from decode. Encode receives that same message envelope with the agent source, under the existing request.response argument. Models and neural hardware determine actual modality support. The protocol does not promise that a text-only model can process audio/video.

Private b2a:// references are resolved automatically through the configured provider's read_resource MCP tool or POST /v1/resources/read HTTP endpoint, with the provider's credential. The request is {"uri":"b2a://session-123/audio-001","consent":true}; the result is one content block of the same type/MIME, containing inline data or a scoped HTTPS URL. No arbitrary URL receives the provider bearer token. Large media should use scoped HTTPS references; providers enforce resource ownership and expiry.

Streaming metadata is stream: {"id":"utterance-1","index":0,"final":false}. Indices start at zero. Snapshots replace previous content; mode: "delta" appends one text block. Only final decoder messages run the agent. Sequence/source conflicts, finalized stream reuse and oversized accumulated input are rejected without execution. See the repository's docs/b2a-protocol.md for the complete wire contract.

Neural provider connection options

B2ACodec supports optional overrides for provider tool names and HTTP paths. Omit them when your provider uses the SDK defaults.

Configuration Applies to SDK defaults
decode_tool, encode_tool MCP tool names decode, encode
decode_path, encode_path HTTP endpoint paths /v1/decode, /v1/encode

For MCP, configure the provider's complete MCP endpoint URL and select the MCP transport. Tool names identify the provider's tools; HTTP path overrides do not affect MCP calls. These illustrative names must match your provider.

neural_service = B2ACodec(
    codec_url,
    token=codec_token,
    transport="mcp",
    decode_tool="neural_decode",
    encode_tool="neural_encode",
)

For HTTP, configure the provider base URL. Paths start with / and are appended to that URL after removing its trailing slash. For example, https://neural.example/api plus /neural/decode calls https://neural.example/api/neural/decode. MCP tool names do not affect HTTP calls.

neural_service = B2ACodec(
    service_url,
    token=codec_token,
    transport="http",
    decode_path="/neural/decode",
    encode_path="/neural/encode",
)

The SDK defaults to HTTP when transport is omitted; the B2A quickstarts above explicitly select MCP. Pass the resulting service as the B2A provider in the same Agent.protocols configuration. These options change routing only: request and response schemas still follow the B2A provider contract. Implement B2AProvider when a vendor needs payload translation. These are neural-service routes, separate from Marona's /runtime/b2a/... endpoints.

Execution and errors

Use Runner.stream for streamed Agent events and RealtimeRunner for a live session. Delegation and handoffs use explicit Agent graphs. Set execution limits and validate permissions before tools with side effects; cancellation cannot roll back an operation that has already executed.

Inspect the canonical error code, HTTP status, request ID and retry metadata. Correct invalid input and authorization errors before retrying. Use bounded retries for transient failures. Never include credentials or document content in error reports.

See Platform documentation for language specific examples and migration guidance. These files are generated from docs/client-sdk-guides.json; verify them with python scripts/sync_client_sdk_docs.py --check.

B2A 1.0 uses the same multimodal message envelope in both directions. The source identifies the sender. SDK-managed streaming, resource resolution and encoder return messages are built in. Existing v0.1 runtime sessions remain supported; new SDK connections require a runtime advertising B2A 1.0.

Metadata

Release files for marona 2.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for marona 2.6.0
File Size Uploaded Provenance Actions
marona-2.6.0.tar.gz 124.6 kB With provenance Details for marona-2.6.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for marona 2.6.0
File Interpreter ABI Platform Actions
marona-2.6.0-py3-none-any.whl Python 3 none any Details for marona-2.6.0-py3-none-any.whl

Total release size: 222.7 kB

Release files / marona-2.6.0.tar.gz

Download URL marona-2.6.0.tar.gz
Size 124.6 kB
Tags Source
SHA-256 checksum
How to use checksums
302e7f159a429d2d117db8cbd5f24c15c4d561ff5d565eb3035be0ae98cbeea2
BLAKE2b-256 checksum
How to use checksums
d9b3a3eea3c8182af6f44b6c0325623c57f3c3accd2088e12ceb84e9aaa61d2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / marona-2.6.0-py3-none-any.whl

Download URL marona-2.6.0-py3-none-any.whl
Size 98.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f7e5a23a1a9a89e23ecea41719afb10bd5072c388da8f853d7214855aba73ffc
BLAKE2b-256 checksum
How to use checksums
3fb160fec8b2cb7afb9f0d72f8e928b52416a2e1e5579cc9604b516efcf2624b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Security for marona 2.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for marona-2.6.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

2.6.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page