Skip to main content

LLM agent framework with structured I/O

Project description

acorn 🌰

LLM agent framework with structured I/O, heavily influenced by DSPy.

Build AI agents with type-safe inputs and outputs, automatic tool calling, and powerful agentic loops.

Tests Coverage Python License


✨ Features

  • 🎯 Structured I/O - Pydantic models for inputs and outputs
  • 🤖 Agentic Loops - Multi-turn execution with tool calling
  • 🛠️ Auto Tool Schemas - Generate from type hints and docstrings
  • 🔄 Dynamic Tools - Add/remove tools during execution
  • Parse Error Recovery - Automatic retry on validation failures
  • 📊 Step Callbacks - Full control over loop behavior
  • 🔌 LiteLLM Integration - Works with any LLM provider
  • 🌊 Streaming Responses - Real-time output with partial structured updates
  • 💾 Provider Caching - Reduce latency and cost with prompt caching

🚀 Quick Start

Installation

pip install acorn

Set your API key:

# For Anthropic Claude (default)
export ANTHROPIC_API_KEY="your-key-here"

# Or for OpenAI
export OPENAI_API_KEY="your-key-here"

# Or any other LiteLLM-supported provider

Single-Turn Example

from pydantic import BaseModel, Field
from acorn import Module

class Input(BaseModel):
    text: str = Field(description="The text to summarize")
    max_words: int = Field(default=100, description="Maximum words in summary")

class Output(BaseModel):
    summary: str = Field(description="The concise summary")
    word_count: int = Field(description="Number of words in summary")

class Summarizer(Module):
    """Summarize text concisely."""

    initial_input = Input
    final_output = Output

# Use it
summarizer = Summarizer()
result = summarizer(
    text="Long article text here...",
    max_words=50
)

print(result.summary)
print(f"Words: {result.word_count}")

Multi-Turn Agentic Loop

from pydantic import BaseModel, Field
from acorn import Module, tool

class Input(BaseModel):
    topic: str = Field(description="Research topic")
    depth: str = Field(default="shallow", description="Research depth")

class Output(BaseModel):
    findings: str = Field(description="Summary of findings")
    sources: list[str] = Field(description="Sources consulted")

class ResearchAgent(Module):
    """Research assistant with tools."""

    initial_input = Input
    max_steps = 5  # Enable agentic loop
    final_output = Output

    @tool
    def search(self, query: str) -> list:
        """Search for information."""
        # Your search implementation
        return ["result1", "result2"]

    @tool
    def analyze(self, data: str) -> str:
        """Analyze collected data."""
        # Your analysis implementation
        return f"Analysis: {data}"

    def on_step(self, step):
        """Called after each step."""
        print(f"Step {step.counter}")

        # Early termination if done
        if len(step.tool_results) >= 3:
            step.finish(
                findings="Sufficient data collected",
                sources=["source1", "source2"]
            )

        return step

# Use it
agent = ResearchAgent()
result = agent(topic="Large Language Models", depth="shallow")

📚 Core Concepts

Module

Base class for LLM agents. Configure with:

  • model - LLM to use (default: Claude Sonnet 4.5)
  • temperature - Sampling temperature
  • max_tokens - Maximum tokens to generate
  • max_steps - Max agentic loop iterations (None = single-turn)
  • initial_input - Pydantic model for input schema
  • final_output - Pydantic model for output schema
  • tools - List of available tools
  • cache - Enable provider-level prompt caching

Tools

Functions the LLM can call:

@tool
def search(query: str, limit: int = 10) -> list:
    """Search for information.

    Args:
        query: The search query
        limit: Maximum results to return
    """
    return search_api(query, limit)

Schema is automatically generated from type hints and docstring.

Step Callback

Control agentic loop execution:

def on_step(self, step):
    # Access step info
    print(f"Step {step.counter}")
    print(f"Tools called: {[tc.name for tc in step.tool_calls]}")

    # Dynamic tool management
    step.add_tool(new_tool)
    step.remove_tool("old_tool")

    # Early termination
    if condition:
        step.finish(result="Early exit")

    return step

🎯 Examples

See examples/ directory:


🧪 Testing

# Run all tests
pytest

# With coverage
pytest --cov=acorn

# Specific test file
pytest tests/test_agentic_loop.py -v

Current status: 128 tests passing, 90% coverage


📖 Documentation


🛣️ Roadmap

✅ Completed

  • Single-turn execution
  • Multi-turn agentic loops
  • Tool calling with auto-schema generation
  • Parse error recovery
  • Dynamic tool management
  • Step callbacks
  • Streaming responses with partial structured output
  • Forced termination strategies
  • Provider caching

📋 Planned

  • Branching workflows
  • Async support

🤝 Contributing

Contributions welcome! Please:

  1. Check open issues for areas to help
  2. Write tests for new features (maintain >80% coverage)
  3. Update documentation
  4. Add examples for new features

💬 Questions?

Check out:


📄 License

MIT License - see LICENSE for details


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

acorn-0.4.4.tar.gz (81.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

acorn-0.4.4-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

File details

Details for the file acorn-0.4.4.tar.gz.

File metadata

  • Download URL: acorn-0.4.4.tar.gz
  • Upload date:
  • Size: 81.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for acorn-0.4.4.tar.gz
Algorithm Hash digest
SHA256 e6caef617b5b4cf2d2cd3e4b9da13d200ecae70cb43b4682b59858cab03c1026
MD5 062bbe24db419bdac4caed9fe0133e71
BLAKE2b-256 707ef76ca49d6cf6afbe99dafc014c8daa6be9b03e2a86f01436bf1720855d29

See more details on using hashes here.

Provenance

The following attestation bundles were made for acorn-0.4.4.tar.gz:

Publisher: publish.yml on askmanu/acorn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file acorn-0.4.4-py3-none-any.whl.

File metadata

  • Download URL: acorn-0.4.4-py3-none-any.whl
  • Upload date:
  • Size: 26.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for acorn-0.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1b0470154fcb725c6b5a00be32e1d59e939f673e4f97bca2dd438b548871b4d3
MD5 99eeb2f8574cfba31613a3778216f361
BLAKE2b-256 99d63f3b4b6989d58bc25564ab05c7ef44c1f0f298a880f7e2743e973997bd34

See more details on using hashes here.

Provenance

The following attestation bundles were made for acorn-0.4.4-py3-none-any.whl:

Publisher: publish.yml on askmanu/acorn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page