n8's blog

marvin 3.x

it's March, and you know what that means...

march 2023

Screenshot 2025-03-29 at 10

march 2024

Screenshot 2025-03-29 at 10

Screenshot 2025-03-29 at 10

march 2025

Screenshot 2025-03-29 at 9

that's right - it's time to get excited about structured outputs... again. More people than ever are discovering the appetite for structured and ambient intelligence, at an accelerated rate likely due in part to how well the OSS ecosystem1 is following SoTA LLM provider capabilities.

so, marvin 3.x huh?

yes - we've replatformed on pydantic-ai which means we now support all LLM providers that pydantic-ai does ๐ŸŽ‰

see examples/hello_*.py for the crash course!

there you'll find:

structured output utils

marvin.extract - extract structured data from text

Perfect for pulling structured information from natural language - like finding entities, parsing details, or converting descriptions into data.

def extract(
    data: Any,
    target: type[T] | None = None,
    instructions: str | None = None,
    agent: Agent | None = None,
    thread: Thread | str | None = None,
    context: dict[str, Any] | None = None,
) -> list[T]: ...
from dataclasses import dataclass
import marvin

@dataclass
class Currency:
    name: str
    symbol: str

if __name__ == "__main__":
    currencies = marvin.extract(
        "After flying ORD to Heathrow, I exchanged my greenbacks for local currency.",
        target=Currency,
    )
    print(currencies)
# [Currency(name='US Dollar', symbol='USD'), Currency(name='British Pound Sterling', symbol='GBP')]
marvin.cast - convert between data formats

Transform data between different schemas and formats - great for API integrations, data migrations, and format standardization.

def cast(
    data: Any,
    target: type[T] | None = None,
    instructions: str | None = None,
    agent: Agent | None = None,
    thread: Thread | str | None = None,
    context: dict[str, Any] | None = None,
) -> T: ...
from pydantic import BaseModel, Field
from typing import Literal

class AllergyFormatA(BaseModel):
    substance: str = Field(description="Name of the allergic substance")
    severity: str = Field(description="Severity level (mild, moderate, severe)")
    reaction: str = Field(description="Type of allergic reaction")
    dateIdentified: str = Field(description="Date allergy was identified (YYYY-MM-DD)")

class AllergyFormatB(BaseModel):
    agent: str = Field(description="Name of the allergic substance")
    riskLevel: Literal["low", "medium", "high"] = Field(description="Risk level")
    manifestations: list[str] = Field(description="List of observed reactions")
    documentation_date: str = Field(description="Date allergy was documented (YYYY-MM-DD)")

allergy_record_a = AllergyFormatA(
    substance="penicillin",
    severity="severe",
    reaction="anaphylaxis",
    dateIdentified="2020-01-01",
)

allergy_record_b = marvin.cast(
    allergy_record_a,
    AllergyFormatB,
    instructions="Convert Epic allergy record to Cerner format. Map 'severe' severity to 'high' risk.",
)
print(allergy_record_b)
# agent='penicillin' riskLevel='high' manifestations=['anaphylaxis'] documentation_date='2020-01-01'
marvin.classify - categorize data into predefined types

Sort and label data into predefined categories - ideal for sentiment analysis, content moderation, and automated triage.

def classify(
    data: Any,
    labels: Sequence[T] | type[T],
    multi_label: bool = False,
    *,
    instructions: str | None = None,
    agent: Agent | None = None,
    thread: Thread | str | None = None,
    context: dict[str, Any] | None = None,
) -> T | list[T]: ...
from enum import Enum

class CustomerDissatisfaction(Enum):
    IMPERCEPTIBLE = 0
    MINIMAL = 1
    MODERATE = 2
    SUBSTANTIAL = 3
    EXTREME = 4

customer_feedbacks = [
    "I really appreciated your service ๐Ÿ™‚",
    "Your hold music made my ears bleed, but eventually got what I needed",
    "Your support team is the Enron of customer service",
]

results = [marvin.classify(f, CustomerDissatisfaction) for f in customer_feedbacks]
print(results)
# [<CustomerDissatisfaction.IMPERCEPTIBLE: 0>, <CustomerDissatisfaction.SUBSTANTIAL: 3>, <CustomerDissatisfaction.EXTREME: 4>]
marvin.generate - create structured data from instructions

Create new structured data from scratch - perfect for content generation, test data creation, and creative tasks.

def generate(
    target: type[T] | None = None,
    n: int = 1,
    instructions: str | None = None,
    agent: Agent | None = None,
    thread: Thread | str | None = None,
    context: dict[str, Any] | None = None,
) -> list[T]: ...
from typing import Annotated
from pydantic import Field

Fruit = Annotated[str, Field(description="A fruit")]

fruits = marvin.generate(target=Fruit, n=3, instructions="high vitamin C content")
print(fruits)
# ['Orange', 'Kiwi', 'Strawberry']

names = marvin.generate(
    target=str,
    n=len(fruits),
    instructions=f"bizarro sitcom character names based on these fruit: {fruits}",
)
print(names)
# ['Orangey McPeelson', 'Kiwi Kookaburra', 'Strawberry Sassafras']

agentic features

marvin.run - the simplest way to use AI

When you just need a quick result - the fastest path from prompt to output.

from typing import Annotated
from pydantic import Field
import marvin

# Get a simple string response
poem = marvin.run("Write a short poem about artificial intelligence")
print(poem)

# Return structured data
cidr = marvin.run(
    "private IPs in my network",
    result_type=Annotated[str, Field(description="IP CIDR blocks", examples=["192.168.1.0/24", "10.0.0.0/8"])],
    context={"user": "naive about networking, only cares about class C private IPs"}
)
print(cidr)
โ•ญโ”€ Agent "Marvin" (7bf99ec8) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                  โ”‚
โ”‚  Tool:    Mark Task 8bae4aee ("Write a short poem about artificial inte...") successful          โ”‚
โ”‚  Status:  โœ…                                                                                     โ”‚
โ”‚  Result   In a world of logic, code, and bright machines, Where silicon dreams pulse through     โ”‚
โ”‚           electric seams, Artificial minds weave wisdom and art, Transforming gears and bytes    โ”‚
โ”‚           into a heart.                                                                          โ”‚
โ”‚                                                                                                  โ”‚
โ”‚           Whispers of algorithms dance in light, Guiding humanity to day from night. With eyes   โ”‚
โ”‚           that see far beyond our sight, A silent partner in our endless flight.                 โ”‚
โ”‚                                                                                                  โ”‚
โ”‚           For AI listens, learns, and gently guides, With boundless knowledge at its side. Yet   โ”‚
โ”‚           still, it yearns for human touch, To understand and feel the world so much.            โ”‚
โ”‚                                                                                                  โ”‚
โ”‚           In this vast symphony of thought and line, May AI and humankind intertwine, To solve   โ”‚
โ”‚           the puzzles of this earthly sphere, Hand in hand, a future bright and clear.           โ”‚
โ”‚                                                                                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  9:40:38 PM โ”€โ•ฏ
In a world of logic, code, and bright machines,
Where silicon dreams pulse through electric seams,
Artificial minds weave wisdom and art,
Transforming gears and bytes into a heart.

Whispers of algorithms dance in light,
Guiding humanity to day from night.
With eyes that see far beyond our sight,
A silent partner in our endless flight.

For AI listens, learns, and gently guides,
With boundless knowledge at its side.
Yet still, it yearns for human touch,
To understand and feel the world so much.

In this vast symphony of thought and line,
May AI and humankind intertwine,
To solve the puzzles of this earthly sphere,
Hand in hand, a future bright and clear.
โ•ญโ”€ Agent "Marvin" (7bf99ec8) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                  โ”‚
โ”‚  Tool:    Mark Task d5578db6 ("private IPs in my network...") successful                         โ”‚
โ”‚  Status:  โœ…                                                                                     โ”‚
โ”‚  Result   192.168.0.0/16                                                                         โ”‚
โ”‚                                                                                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  9:40:45 PM โ”€โ•ฏ
192.168.0.0/16
marvin.Agent - create AI assistants that use tools

For when you need the chat completion API to take action for you - using context/memory/tools to achieve strongly-typed outputs.

e.g.

from pathlib import Path
import marvin
from pydantic_ai.models.openai import OpenAIModel
from pydantic_ai.providers.openai import OpenAIProvider

def write_file(path: str, content: str):
    Path(path).write_text(content)

writer = marvin.Agent(
    model=OpenAIModel(
        "gpt-4o",
        provider=OpenAIProvider(
            api_key=os.getenv("OPENAI_API_KEY"),
            base_url="https://api.openai.com/v1",
            http_client=httpx.AsyncClient(
                # proxy="http://localhost:8080",
                # headers={"x-SOME-HEADER": "some-value"},
            ),
        ),
    ),
    name="Technical Writer",
    instructions="Write concise, engaging content for developers",
    tools=[write_file],
)

marvin.run("how to use pydantic? write haiku to docs.md", agents=[writer])
ยป uv run examples/hello_agent.py
โ•ญโ”€ Agent "Technical Writer" (d9cf5814) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                  โ”‚
โ”‚  Tool:    write_file                                                                             โ”‚
โ”‚  Status:  โœ…                                                                                     โ”‚
โ”‚  Input    {                                                                                      โ”‚
โ”‚               'path': 'docs.md',                                                                 โ”‚
โ”‚               'content': '### Pydantic Haiku\n\nModel your data.\nValidation made                โ”‚
โ”‚           easy.\nPydantic shines bright.\n'                                                      โ”‚
โ”‚           }                                                                                      โ”‚
โ”‚                                                                                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  4:35:45 PM โ”€โ•ฏ
โ•ญโ”€ Agent "Technical Writer" (d9cf5814) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                  โ”‚
โ”‚  Tool:    Mark Task 8b1894c6 ("how to use pydantic? write haiku to docs...") successful          โ”‚
โ”‚  Status:  โœ…                                                                                     โ”‚
โ”‚  Result   Pydantic haiku added to docs.md                                                        โ”‚
โ”‚                                                                                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  4:35:48 PM โ”€โ•ฏ
marvin.Thread - maintain conversation state

Perfect for multi-turn interactions where context matters.

import marvin

with marvin.Thread("user-123"):
    # First message
    marvin.run("What are the top 3 programming languages?")
    
    # Follow-up that references previous context
    marvin.run("Why is the first one so popular?")
โ•ญโ”€ Agent "Marvin" (7bf99ec8) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                  โ”‚
โ”‚  Tool:    Mark Task 73b640dd ("What are the top 3 programming languages...") successful          โ”‚
โ”‚  Status:  โœ…                                                                                     โ”‚
โ”‚  Result   As of 2023, the top 3 programming languages are:                                       โ”‚
โ”‚                                                                                                  โ”‚
โ”‚            1 Python                                                                              โ”‚
โ”‚            2 JavaScript                                                                          โ”‚
โ”‚            3 Java                                                                                โ”‚
โ”‚                                                                                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  9:42:02 PM โ”€โ•ฏ
โ•ญโ”€ Agent "Marvin" (7bf99ec8) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                  โ”‚
โ”‚  Tool:    Mark Task 4f636d92 ("Why is the first one so popular?...") successful                  โ”‚
โ”‚  Status:  โœ…                                                                                     โ”‚
โ”‚  Result   Python is popular for several reasons:                                                 โ”‚
โ”‚                                                                                                  โ”‚
โ”‚            1 Ease of Learning and Use: Python's simple and readable syntax makes it an           โ”‚
โ”‚              excellent language for beginners and allows developers to write code more quickly.  โ”‚
โ”‚            2 Versatility: Python is highly versatile and can be used in various fields such as   โ”‚
โ”‚              web development, data analysis, artificial intelligence, scientific computing, and  โ”‚
โ”‚              more.                                                                               โ”‚
โ”‚            3 Community and Libraries: Python has a large and active community that contributes   โ”‚
โ”‚              to a rich ecosystem of libraries and frameworks, which accelerates development and  โ”‚
โ”‚              problem-solving for a variety of tasks.                                             โ”‚
โ”‚            4 Industry Adoption: Many companies and organizations, including tech giants like     โ”‚
โ”‚              Google, use Python extensively, further establishing its credibility and demand in  โ”‚
โ”‚              the industry.                                                                       โ”‚
โ”‚            5 Cross-Platform Compatibility: Python works on multiple platforms, making it a       โ”‚
โ”‚              convenient choice for cross-platform development.                                   โ”‚
โ”‚                                                                                                  โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  9:42:03 PM โ”€โ•ฏ

things we're trying out in 3.x

See here for some context on teams, plans and some even higher-level new features.

that's it!

Checkout the rest of the examples for more, and let us know if you have any questions!

FAQ

"what happened to controlflow?"

controlflow implicitly used prefect under the hood to instrument LLM calls, which is nice for those who already use prefect for their orchestration needs, but those who don't found the overhead frustrating. marvin now offers all the agentic features that controlflow did, but without that overhead and with additional LLM provider support via pydantic-ai. also, you can now trivially use logfire for OTEL observability. you're still free to deploy workflows via prefect primitives if you're so inclined.


"didn't the while loops get obsoleted by MCP?"

while one could reasonably argue that DSPy or MCP offer more sophisticated or granular control over LLMs, I would argue that marvin occupies a different space. A lot of people (more than ever) write python to solve data problems, and marvin is an extremely flexible and easy way to quickly bring self-documenting LLM-power into any code without skewing the codebase too much towards a very specific "AI paradigm". Also, these tools can be used together according to their strengths.


"Why not just pydantic-ai?

we already had this API, it's just easier to implement now. we just find it convenient


"are there major breaking changes in 3.x?"

not really, just be sure to clear any old ~/.marvin/marvin.db files you have laying around from older versions. we only added to the public API 2. The (beta) OpenAI assistants API was superseded by the pydantic-ai agent paradigm.


"where are the docs for 3.x?"

on the way - sorry about that! you can run them locally:

gh repo clone prefecthq/marvin
cd marvin
just serve-docs

EDIT: now they're here


"what's next for marvin?"

firstly, we should publish the docs we currently have at docs/ to askmarvin.ai. secondly, adding more examples and tutorials to the repo, and perhaps add a logfire cookbook / full-fledged integration. What do you want to see? We are very open to ideas and would happily answer any questions that come up as you work through a contribution.

...

  1. I mean "OSS" very hand-wavily, including DeepSeek for example

  2. -ish, feel free to open an issue if you find something is missing.