Pydantic Agents -> ISPL


Through a combination of inspection and LLM-backed completion, py-mcmas can guess about how to write ISPL specs from your existing pydantic-agents. This process is experimental and probably best thought of as semi-automatic at best, and your milage may vary depending on how powerful your LLM backend is!

If you're interested in something more deterministic, see the demo for gradual annotation of agent-frameworks.

Intro to Societies


For starters, you'll probably want to enumerate existing agents, and that's part of what api://mcmas.Society does. See the example below:

Summary
import pydantic_ai

# Declare pydantic agents as usual
alice = pydantic_ai.Agent(name="alice")
bob = pydantic_ai.Agent(name="bob")


# Declare pydantic tools as usual
@alice.tool
def tool1(expected="", arguments="", **kwargs) -> str:
    return f"tool1 response {expected} {arguments}"


@bob.tool
def tool2(different="", arguments="", **kwargs) -> str:  # noqa
    return f"tool2 response {different} {arguments}"


# Create society from module
from mcmas import ai

society = ai.Society(pydantic_ai)


def test_society_membership():
    assert alice in society, "failed to detect agent"
    assert bob in society, "failed to detect agent"


def test_society_iterable():
    for agent in [alice, bob]:
        assert agent in society, "society should be populated and iterable"

Code to Specification


Using constructor notation you can get specification objects from pydantic agents like this:

Summary
import mcmas

import pydantic_ai

# Declare agents & tools as usual
alice = pydantic_ai.Agent(name="alice")
bob = pydantic_ai.Agent(name="bob")


@alice.tool
def tool1(*args, **kwargs) -> str:
    return f"tool1 {[args,kwargs]}"


@bob.tool
def tool2(ctx, first_kwarg: int = 1, **kwargs) -> str:
    return f"tool2 {[ctx, first_kwarg, kwargs]}"


def test_agent_from_pydantic():
    """
    Using square-brackets notation, construct
    an agent spec from an agent implementation
    """
    alice_prime = mcmas.ispl.Agent[alice]
    assert isinstance(alice_prime, (mcmas.Agent,))


def test_agent_complete():
    bob_prime = mcmas.Agent[bob]
    assert bob_prime.protocol, "default protocol should be set"
    assert not bob_prime.advice, "no advice, agent is concrete"
    assert bob_prime.concrete, "expected agent validates and is ready-to-run"
    expected = "Agent bob" in bob_prime.model_dump_source()
    err = "agent name extracted incorrectly"
    assert expected, err
    err = "agent-implementation's argument should be reflected in spec"
    expected = "first_kwarg" in bob_prime.vars
    assert expected, err
    tmp = mcmas.Agent.load_from_source(bob_prime.model_dump_source())
    assert "Other" in str(tmp.protocol)