Getting started

Praval has two entry paths. Start with direct Agent calls when one agent is enough. Add Reef and Spores when independent specialists need to collaborate.

Install

python -m pip install praval

Praval supports Python 3.9 through 3.13. Optional capabilities are installed separately:

python -m pip install "praval[memory]"
python -m pip install "praval[storage]"
python -m pip install "praval[mcp]"  # Python 3.10+

For a hosted provider, set the corresponding environment variable before running an example:

export OPENAI_API_KEY="..."
# or ANTHROPIC_API_KEY, COHERE_API_KEY, or GEMINI_API_KEY

Passing provider and model explicitly makes an example reproducible. If they are omitted, Praval resolves its configured defaults and available credentials.

First path: call a model through Agent

from praval import Agent

assistant = Agent(
    "assistant",
    provider="openai",
    model="gpt-5.4-mini",
    system_message="Be concise.",
)
try:
    response = assistant.generate("Explain what a Praval Spore carries.")
    print(response.content)
    print(response.provider, response.model, response.usage)
finally:
    assistant.close()

Agent.generate() returns ModelResponse. Its content, provider, model, usage, tool calls, and raw provider data are available without changing the application contract for each provider.

The compatibility method returns only a string:

text = assistant.chat("Explain Reef in one sentence.")

Prefer generate() for new code. Use agenerate(), stream(), and astream() for asynchronous work and normalized streaming events.

Structured output

import json

response = assistant.generate(
    "Return one fact about Reef as JSON.",
    response_schema={
        "type": "object",
        "properties": {"fact": {"type": "string"}},
        "required": ["fact"],
    },
)
fact = json.loads(response.content)["fact"]

The runtime checks that the selected provider profile supports structured output and then sends the schema as a provider constraint. It does not run a second local JSON Schema validation pass.

Second path: collaborate through Reef

A Spore contains routing fields and a structured knowledge dictionary. The conventional knowledge["type"] value lets decorated agents decide which messages to handle.

from praval import agent, broadcast, get_reef, start_agents


@agent("researcher", provider="ollama", responds_to=["research_request"])
def researcher(spore):
    topic = spore.knowledge["topic"]
    broadcast(
        {
            "type": "research_complete",
            "topic": topic,
            "finding": f"Evidence collected for {topic}",
        }
    )


@agent("editor", provider="ollama", responds_to=["research_complete"])
def editor(spore):
    print(spore.knowledge["finding"])


start_agents(
    researcher,
    editor,
    initial_data={"type": "research_request", "topic": "agent systems"},
)
reef = get_reef()
reef.wait_for_completion(timeout=30)
reef.shutdown()

This example uses the Ollama preset only to construct credential-free message handlers; it does not call the local model server. A decorated handler may call chat() or achat() when it actually needs model output.

wait_for_completion() waits for submitted Reef work. Always shut down Reef and other resources explicitly in scripts and tests.

When to use each layer

Need

Start with

One conversational or tool-using agent

Agent

Provider-neutral metadata and usage

Agent.generate()

Streaming deltas and final events

Agent.stream() / Agent.astream()

Multiple reactive specialists

@agent, Reef, and Spores

Deterministic application cleanup

PravalApp

External MCP tools

praval.mcp.MCPClient with async Agent calls

PravalApp owns agents and a Reef for cleanup. In this release it does not isolate provider registries or redirect agent Reef helpers; see Application lifecycle.

Local models

Praval connects to an already-running OpenAI-compatible server:

from praval import Agent

local = Agent("local", provider="ollama", model="llama3")
try:
    print(local.chat("Say hello."))
finally:
    local.close()

The presets are ollama, vllm, lmstudio, and llama-cpp. Text and streaming are enabled by default. Tools, structured output, reasoning, and multimodal input require explicit capability configuration for the server you are using.

Learn by inspecting execution

The Jupyter course explains Agent, Reef, Spore, tools, memory, HITL, MCP, and voice flows with visible runtime state. Start with examples/notebooks/course/00_architecture.ipynb.

For a credential-free runtime check:

python examples/model_runtime_fake_provider.py

Next, read Core concepts and API layers, Model Runtime, and the generated API Reference.