Thank you for your interest in contributing to soothe-plugins! This guide will help you add new plugins, tools, and subagents.
Create a new package in src/soothe_plugins/your_plugin/:
src/soothe_plugins/
├── __init__.py
├── paperscout/
│ └── ...
└── your_plugin/
├── __init__.py # Plugin class with @plugin decorator
├── events.py # Custom events (optional)
├── models.py # Data models
├── state.py # State definitions (if subagent)
├── implementation.py # Core logic
└── README.md # Plugin documentation
Create your plugin class in __init__.py:
from soothe_sdk.plugin import plugin, subagent, tool
@plugin(
name="your_plugin",
version="1.0.0",
description="Brief description of your plugin",
dependencies=[
"required-package>=1.0.0",
],
trust_level="standard", # or "trusted" for privileged plugins
)
class YourPlugin:
"""Your plugin implementation."""
async def on_load(self, context):
"""Called when plugin is loaded.
Args:
context: Plugin context with config, logger, and utilities.
"""
# Validate dependencies
# Initialize resources
context.logger.info("Your plugin loaded")
@subagent(
name="your_agent",
description="What your agent does",
model="openai:gpt-4o-mini",
)
async def create_subagent(self, model, config, context, **kwargs):
"""Create your subagent.
Returns:
Subagent dict with name, description, and runnable.
"""
from .implementation import create_your_subagent
return create_your_subagent(config=config, **kwargs)
@tool(
name="your_tool",
description="What your tool does",
)
def your_tool(self, arg: str) -> str:
"""Your tool implementation."""
return f"Result: {arg}"Add entry point to pyproject.toml:
[project.entry-points."soothe.plugins"]
your_plugin = "soothe_plugins.your_plugin:YourPlugin"Add required packages to dependencies in pyproject.toml:
dependencies = [
# ... existing dependencies ...
"your-required-package>=1.0.0",
]Create test suite in tests/test_your_plugin/:
tests/
├── conftest.py
├── test_paperscout/
│ └── ...
└── test_your_plugin/
├── conftest.py # Plugin-specific fixtures
├── test_events.py # Event tests
├── test_models.py # Model tests
├── test_plugin.py # Plugin lifecycle tests
└── test_implementation.py # Core logic tests
For agent-based plugins:
from langgraph.graph import StateGraph
from soothe_deepagents import CompiledSubAgent
def create_your_subagent(config, **kwargs) -> dict:
"""Create and return a compiled subagent.
Returns:
Dict with 'name', 'description', and 'runnable' (CompiledSubAgent).
"""
# Define state
# Create graph
# Add nodes and edges
# Compile subagent
return {
"name": "your_agent",
"description": "What it does",
"runnable": compiled_graph,
}For simple tool-based plugins:
from langchain_core.tools import tool
@tool
def your_tool(arg: str) -> str:
"""Tool description.
Args:
arg: Argument description.
Returns:
Result description.
"""
# Tool implementation
return result# Clone repository
git clone <soothe-plugins-repo>
cd soothe-plugins
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # or `source .venv/bin/activate.fish`
# Install in development mode
pip install -e ".[dev]"# Create plugin package
mkdir -p src/soothe_plugins/your_plugin
# Implement plugin
# Add tests
# Run tests
pytest tests/test_your_plugin/
# Format code
ruff format src/soothe_plugins/your_plugin/
# Lint code
ruff check --fix src/soothe_plugins/your_plugin/# Test with Soothe
pip install -e /path/to/soothe
soothe checkhealth
# Run your plugin
soothe "test query" --subagent your_agent# tests/test_your_plugin/test_plugin.py
import pytest
from soothe_plugins.your_plugin import YourPlugin
@pytest.fixture
def plugin():
"""Create plugin instance."""
return YourPlugin()
@pytest.mark.asyncio
async def test_plugin_load(plugin, mock_soothe_config):
"""Test plugin can load."""
context = MagicMock()
context.logger = MagicMock()
await plugin.on_load(context)
# Verify initialization
def test_plugin_metadata(plugin):
"""Test plugin metadata."""
assert plugin.name == "your_plugin"
assert plugin.version == "1.0.0"- Unit tests: Test individual functions and classes
- Integration tests: Test plugin with Soothe framework
- Event tests: Test custom event emission
- Error handling: Test error cases
- Python >=3.11
- Type hints on all public functions
- Google-style docstrings
- Ruff for formatting and linting
- Line length: 120 characters
def your_function(arg: str, optional: int = 0) -> dict:
"""Brief description.
Args:
arg: Description of arg.
optional: Description of optional parameter.
Returns:
Description of return value.
Raises:
ValueError: When arg is invalid.
"""
passRegister custom events in events.py:
from soothe.core.event_catalog import register_event
from soothe_sdk.core.events import SootheEvent
class YourCustomEvent(SootheEvent):
type: str = "soothe.community.your_plugin.custom"
data: str
# Register at module load time
register_event(
YourCustomEvent,
summary_template="Custom event: {data}",
)- Fork the repository
- Create a feature branch (
git checkout -b feature/your-plugin) - Commit changes (
git commit -m "Add your_plugin") - Push to branch (
git push origin feature/your-plugin) - Open a Pull Request
- Code follows style guidelines
- Tests added and passing
- Documentation updated
- Entry points registered
- Dependencies added to
pyproject.toml
- Issues: Open an issue on GitHub
- Documentation: See Soothe RFCs and implementation guides
- Examples: Check
sample_echo/for minimal reference implementation
By contributing, you agree that your contributions will be licensed under the MIT License.