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.
✨ 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 temperaturemax_tokens- Maximum tokens to generatemax_steps- Max agentic loop iterations (None = single-turn)initial_input- Pydantic model for input schemafinal_output- Pydantic model for output schematools- List of available toolscache- 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:
single_turn_summarizer.py- Basic single-turn usageresearch_assistant.py- Full agentic loop with tools
🧪 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
- Getting Started - Installation and first steps
- Module Reference - Complete Module API 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:
- Check open issues for areas to help
- Write tests for new features (maintain >80% coverage)
- Update documentation
- Add examples for new features
💬 Questions?
Check out:
- Getting Started for installation and examples
- Module Reference for detailed API docs
- Examples for working code
- Tests for usage patterns
📄 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file acorn-0.4.3.tar.gz.
File metadata
- Download URL: acorn-0.4.3.tar.gz
- Upload date:
- Size: 81.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51b9ae18080235381601bbbd6003fe7a7a88019fd74d9bb6e88fe3093b41a89c
|
|
| MD5 |
1b2953d035a2cffcc147fe32c175b5a6
|
|
| BLAKE2b-256 |
fd0542ebb93d01533c6e5b889f532280913e1d7b25dbb7bf53f6cb8930299127
|
Provenance
The following attestation bundles were made for acorn-0.4.3.tar.gz:
Publisher:
publish.yml on askmanu/acorn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
acorn-0.4.3.tar.gz -
Subject digest:
51b9ae18080235381601bbbd6003fe7a7a88019fd74d9bb6e88fe3093b41a89c - Sigstore transparency entry: 934667109
- Sigstore integration time:
-
Permalink:
askmanu/acorn@f014a34447c4da2a5a21763574284ad905e71c9c -
Branch / Tag:
refs/tags/0.4.3 - Owner: https://github.com/askmanu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f014a34447c4da2a5a21763574284ad905e71c9c -
Trigger Event:
release
-
Statement type:
File details
Details for the file acorn-0.4.3-py3-none-any.whl.
File metadata
- Download URL: acorn-0.4.3-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a56e2cc80f4ada63aff3c3fad60b40841cb14581abb95c9b1b0d984f9f2491c
|
|
| MD5 |
59ee6377e4fc9cf6927ee206ec4aa5c5
|
|
| BLAKE2b-256 |
0285e03c672a414a342ac1b842111503dd2906ff152c86699037cfce61d729e1
|
Provenance
The following attestation bundles were made for acorn-0.4.3-py3-none-any.whl:
Publisher:
publish.yml on askmanu/acorn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
acorn-0.4.3-py3-none-any.whl -
Subject digest:
1a56e2cc80f4ada63aff3c3fad60b40841cb14581abb95c9b1b0d984f9f2491c - Sigstore transparency entry: 934667171
- Sigstore integration time:
-
Permalink:
askmanu/acorn@f014a34447c4da2a5a21763574284ad905e71c9c -
Branch / Tag:
refs/tags/0.4.3 - Owner: https://github.com/askmanu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f014a34447c4da2a5a21763574284ad905e71c9c -
Trigger Event:
release
-
Statement type: