soothe-plugins 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ # Plugin Template
2
+
3
+ This directory serves as a template for creating new plugins in soothe-plugins.
4
+
5
+ ## Quick Start
6
+
7
+ 1. Copy this template:
8
+ ```bash
9
+ cp -r src/soothe_plugins/.plugin_template src/soothe_plugins/your_plugin
10
+ ```
11
+
12
+ 2. Rename files:
13
+ - `__init__.py.template` → `__init__.py`
14
+ - `events.py.template` → `events.py`
15
+ - `models.py.template` → `models.py`
16
+ - `state.py.template` → `state.py`
17
+ - `implementation.py.template` → `implementation.py`
18
+
19
+ 3. Update content:
20
+ - Replace `your_plugin` with your plugin name
21
+ - Replace `YourPlugin` with your class name
22
+ - Add your dependencies to `pyproject.toml`
23
+ - Register entry point in `pyproject.toml`
24
+
25
+ 4. Add tests in `tests/test_your_plugin/`
26
+
27
+ 5. Update documentation in your plugin's `README.md`
28
+
29
+ ## Template Files
30
+
31
+ - `__init__.py.template` - Plugin class with decorators
32
+ - `events.py.template` - Custom events
33
+ - `models.py.template` - Data models
34
+ - `state.py.template` - State definitions (for subagents)
35
+ - `implementation.py.template` - Core logic
36
+ - `README.md.template` - Plugin documentation
37
+
38
+ ## Example: PaperScout Plugin
39
+
40
+ See `src/soothe_plugins/paperscout/` for a complete example.
@@ -0,0 +1,152 @@
1
+ # Your Plugin Name
2
+
3
+ Brief description of your plugin and what it does.
4
+
5
+ ## Features
6
+
7
+ - Feature 1
8
+ - Feature 2
9
+ - Feature 3
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install soothe-community
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ Add to your Soothe `config.yml`:
20
+
21
+ ```yaml
22
+ subagents:
23
+ your_agent:
24
+ enabled: true
25
+ model: "openai:gpt-4o-mini"
26
+ config:
27
+ option1: value1
28
+ option2: value2
29
+ max_items: 100
30
+ ```
31
+
32
+ ### Configuration Options
33
+
34
+ | Option | Type | Default | Description |
35
+ |--------|------|---------|-------------|
36
+ | `option1` | str | "default_value" | Description of option1 |
37
+ | `option2` | int | 10 | Description of option2 |
38
+ | `max_items` | int | 100 | Maximum items to process |
39
+
40
+ ## Usage
41
+
42
+ ### As Subagent
43
+
44
+ ```bash
45
+ # Via TUI (default)
46
+ soothe "your query here" --subagent your_agent
47
+
48
+ # Headless mode
49
+ soothe "your query" --subagent your_agent --no-tui
50
+ ```
51
+
52
+ ### As Tool
53
+
54
+ The plugin also provides tools that can be used directly:
55
+
56
+ ```python
57
+ from soothe_community.your_plugin import YourPlugin
58
+
59
+ plugin = YourPlugin()
60
+ result = plugin.your_tool("input")
61
+ ```
62
+
63
+ ## Examples
64
+
65
+ ### Example 1: Basic Usage
66
+
67
+ ```bash
68
+ soothe "process data" --subagent your_agent
69
+ ```
70
+
71
+ ### Example 2: Advanced Usage
72
+
73
+ ```bash
74
+ soothe "complex operation with parameters" --subagent your_agent --config custom_config.yml
75
+ ```
76
+
77
+ ## Architecture
78
+
79
+ Your plugin consists of:
80
+
81
+ - **Plugin Class**: Main entry point with `@plugin` decorator
82
+ - **Subagent**: Agent graph for complex workflows
83
+ - **Tools**: Individual tools for simple operations
84
+ - **Events**: Custom events for monitoring
85
+
86
+ ## Development
87
+
88
+ ### Setup
89
+
90
+ ```bash
91
+ # Clone soothe-community
92
+ git clone <repo>
93
+ cd soothe-community
94
+
95
+ # Install in dev mode
96
+ pip install -e ".[dev]"
97
+ ```
98
+
99
+ ### Testing
100
+
101
+ ```bash
102
+ pytest tests/test_your_plugin/
103
+ ```
104
+
105
+ ### Code Quality
106
+
107
+ ```bash
108
+ # Format
109
+ ruff format src/soothe_community/your_plugin/
110
+
111
+ # Lint
112
+ ruff check --fix src/soothe_community/your_plugin/
113
+ ```
114
+
115
+ ## API Reference
116
+
117
+ ### YourPlugin
118
+
119
+ Main plugin class.
120
+
121
+ #### Methods
122
+
123
+ - `on_load(context)`: Initialize plugin
124
+ - `create_subagent(model, config, context, **kwargs)`: Create subagent
125
+ - `your_tool(arg)`: Tool function
126
+
127
+ ### YourConfig
128
+
129
+ Configuration model.
130
+
131
+ ### YourState
132
+
133
+ State model for subagent.
134
+
135
+ ## Dependencies
136
+
137
+ - `soothe>=0.1.0` - Core framework
138
+ - `langgraph>=0.2.0` - Graph orchestration
139
+ - `required-package>=1.0.0` - Your dependency
140
+
141
+ ## License
142
+
143
+ MIT
144
+
145
+ ## Support
146
+
147
+ - **Issues**: GitHub Issues
148
+ - **Documentation**: See `docs/` in soothe-community repo
149
+
150
+ ## Contributing
151
+
152
+ See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines.
@@ -0,0 +1,174 @@
1
+ """Your plugin description.
2
+
3
+ Detailed description of what your plugin does, its features, and use cases.
4
+
5
+ Installation:
6
+ pip install soothe-community
7
+
8
+ Configuration (config.yml):
9
+ subagents:
10
+ your_agent:
11
+ enabled: true
12
+ model: "openai:gpt-4o-mini"
13
+ config:
14
+ option1: value1
15
+ option2: value2
16
+
17
+ Usage:
18
+ soothe "your query" --subagent your_agent
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ from typing import Any
25
+
26
+ from soothe_sdk.core.exceptions import PluginError
27
+ from soothe_sdk.plugin import plugin, subagent, tool
28
+
29
+ __all__ = [
30
+ "YourPlugin",
31
+ ]
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ @plugin(
37
+ name="your_plugin",
38
+ version="1.0.0",
39
+ description="Brief description of your plugin",
40
+ dependencies=[
41
+ "required-package>=1.0.0",
42
+ ],
43
+ trust_level="standard",
44
+ )
45
+ class YourPlugin:
46
+ """Your plugin implementation.
47
+
48
+ This plugin provides:
49
+ - Feature 1
50
+ - Feature 2
51
+ - Feature 3
52
+ """
53
+
54
+ async def on_load(self, context: Any) -> None:
55
+ """Validate dependencies and initialize plugin.
56
+
57
+ Args:
58
+ context: Plugin context with config, logger, and utilities.
59
+
60
+ Raises:
61
+ PluginError: If required dependencies are not installed.
62
+ """
63
+ context.logger.info("Loading your_plugin...")
64
+
65
+ # Validate dependencies
66
+ missing_deps = []
67
+
68
+ try:
69
+ import required_package # noqa: F401
70
+ except ImportError:
71
+ missing_deps.append("required-package>=1.0.0")
72
+
73
+ if missing_deps:
74
+ msg = (
75
+ f"Missing required dependencies: {', '.join(missing_deps)}. "
76
+ "Install with: pip install soothe-community"
77
+ )
78
+ raise PluginError(msg)
79
+
80
+ context.logger.info("your_plugin loaded successfully")
81
+
82
+ @subagent(
83
+ name="your_agent",
84
+ description=(
85
+ "Detailed description of what your agent does and when to use it. "
86
+ "This helps the orchestrator decide when to invoke your subagent."
87
+ ),
88
+ model="openai:gpt-4o-mini",
89
+ )
90
+ async def create_subagent(
91
+ self,
92
+ model: Any,
93
+ config: Any,
94
+ context: Any,
95
+ **kwargs: Any,
96
+ ) -> dict[str, Any]:
97
+ """Create your subagent.
98
+
99
+ Args:
100
+ model: Resolved model (BaseChatModel or string).
101
+ config: Soothe configuration.
102
+ context: Plugin context.
103
+ **kwargs: Additional configuration (store, user_id, etc.).
104
+
105
+ Returns:
106
+ Subagent dict with name, description, and runnable.
107
+ """
108
+ from .implementation import create_your_subagent
109
+ from .state import YourConfig
110
+
111
+ # Get plugin configuration
112
+ plugin_config = None
113
+ if hasattr(config, "subagents") and "your_agent" in config.subagents:
114
+ subagent_config = config.subagents["your_agent"]
115
+ if subagent_config.enabled and subagent_config.config:
116
+ plugin_config = YourConfig(**subagent_config.config)
117
+
118
+ if not plugin_config:
119
+ plugin_config = YourConfig()
120
+
121
+ # AsyncPersistStore from plugin context (daemon injects persistence)
122
+ store = kwargs.get("store")
123
+ if not store and hasattr(config, "services"):
124
+ store = config.services.get("persistence")
125
+ if not store:
126
+ msg = "your_agent requires AsyncPersistStore (e.g. kwargs['store'] or config.services)"
127
+ raise ValueError(msg)
128
+
129
+ user_id = kwargs.get("user_id", "default")
130
+
131
+ # Create subagent
132
+ subagent_dict = create_your_subagent(
133
+ config=plugin_config,
134
+ store=store,
135
+ user_id=user_id,
136
+ )
137
+
138
+ context.logger.info(
139
+ f"Created your_agent subagent for user {user_id}"
140
+ )
141
+
142
+ return subagent_dict
143
+
144
+ @tool(
145
+ name="your_tool",
146
+ description="Brief description of what your tool does",
147
+ )
148
+ def your_tool(self, arg: str) -> str:
149
+ """Your tool implementation.
150
+
151
+ Args:
152
+ arg: Description of argument.
153
+
154
+ Returns:
155
+ Description of return value.
156
+ """
157
+ # Tool logic here
158
+ return f"Result: {arg}"
159
+
160
+ def get_subagents(self) -> list[Any]:
161
+ """Get list of subagent factory functions.
162
+
163
+ Returns:
164
+ List containing subagent factory methods.
165
+ """
166
+ return [self.create_subagent]
167
+
168
+ def get_tools(self) -> list[Any]:
169
+ """Get list of tool functions.
170
+
171
+ Returns:
172
+ List containing tool methods.
173
+ """
174
+ return [self.your_tool]
@@ -0,0 +1,34 @@
1
+ """Custom events for your plugin.
2
+
3
+ Register plugin-specific events that can be emitted during execution.
4
+ """
5
+
6
+ from typing import Literal
7
+
8
+ from pydantic import ConfigDict
9
+
10
+ from soothe_sdk.core.events import SubagentEvent
11
+ from soothe_sdk.core.verbosity import VerbosityTier
12
+ from soothe_sdk.plugin.registry import register_event
13
+
14
+
15
+ class YourCustomEvent(SubagentEvent):
16
+ """Custom event for your plugin.
17
+
18
+ Attributes:
19
+ type: Event type identifier.
20
+ data: Event data.
21
+ """
22
+
23
+ type: Literal["soothe.community.your_plugin.custom"] = "soothe.community.your_plugin.custom"
24
+ data: str = ""
25
+
26
+ model_config = ConfigDict(extra="allow")
27
+
28
+
29
+ # Register events at module load time
30
+ register_event(
31
+ YourCustomEvent,
32
+ verbosity=VerbosityTier.NORMAL,
33
+ summary_template="Your plugin event: {data}",
34
+ )
@@ -0,0 +1,112 @@
1
+ """Implementation of your plugin's core logic.
2
+
3
+ This module contains the factory function to create your subagent
4
+ and the node functions that make up your agent graph.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from deepagents import CompiledSubAgent
10
+ from langgraph.graph import END, StateGraph
11
+
12
+ from .events import YourCustomEvent
13
+ from .state import YourConfig, YourState
14
+
15
+
16
+ def create_your_subagent(
17
+ config: YourConfig,
18
+ store: Any = None,
19
+ user_id: str = "default",
20
+ ) -> dict[str, Any]:
21
+ """Create and compile your subagent.
22
+
23
+ Args:
24
+ config: Plugin configuration.
25
+ store: Persistence store for state.
26
+ user_id: User identifier.
27
+
28
+ Returns:
29
+ Dict with name, description, and runnable (CompiledSubAgent).
30
+ """
31
+ # Create the graph
32
+ graph = StateGraph(YourState)
33
+
34
+ # Add nodes
35
+ graph.add_node("initialize", initialize_node)
36
+ graph.add_node("process", process_node)
37
+ graph.add_node("finalize", finalize_node)
38
+
39
+ # Define edges
40
+ graph.set_entry_point("initialize")
41
+ graph.add_edge("initialize", "process")
42
+ graph.add_edge("process", "finalize")
43
+ graph.add_edge("finalize", END)
44
+
45
+ # Compile the graph
46
+ compiled = graph.compile(
47
+ store=store,
48
+ checkpointer=None, # Add checkpointer if needed
49
+ )
50
+
51
+ return {
52
+ "name": "your_agent",
53
+ "description": "What your agent does",
54
+ "runnable": compiled,
55
+ }
56
+
57
+
58
+ async def initialize_node(state: YourState) -> dict[str, Any]:
59
+ """Initialize node: setup and validate input.
60
+
61
+ Args:
62
+ state: Current state.
63
+
64
+ Returns:
65
+ State updates.
66
+ """
67
+ # Initialize processing
68
+ # Validate inputs
69
+ # Setup resources
70
+
71
+ return {
72
+ "results": [],
73
+ "errors": [],
74
+ }
75
+
76
+
77
+ async def process_node(state: YourState) -> dict[str, Any]:
78
+ """Process node: main processing logic.
79
+
80
+ Args:
81
+ state: Current state.
82
+
83
+ Returns:
84
+ State updates.
85
+ """
86
+ # Main processing logic
87
+ # Call external APIs
88
+ # Process data
89
+
90
+ # Emit custom event
91
+ event = YourCustomEvent(data="Processing complete")
92
+ # Note: Events are emitted via the event system
93
+
94
+ return {
95
+ "results": [result],
96
+ }
97
+
98
+
99
+ async def finalize_node(state: YourState) -> dict[str, Any]:
100
+ """Finalize node: cleanup and summary.
101
+
102
+ Args:
103
+ state: Current state.
104
+
105
+ Returns:
106
+ State updates.
107
+ """
108
+ # Cleanup resources
109
+ # Generate summary
110
+ # Prepare output
111
+
112
+ return {}
@@ -0,0 +1,39 @@
1
+ """Data models for your plugin.
2
+
3
+ Define Pydantic models for structured data used by your plugin.
4
+ """
5
+
6
+ from datetime import datetime
7
+ from typing import Any, Optional
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class YourDataModel(BaseModel):
13
+ """Sample data model.
14
+
15
+ Attributes:
16
+ id: Unique identifier.
17
+ name: Name or title.
18
+ created_at: Creation timestamp.
19
+ metadata: Optional metadata.
20
+ """
21
+
22
+ id: str
23
+ name: str
24
+ created_at: datetime = Field(default_factory=datetime.now)
25
+ metadata: Optional[dict[str, Any]] = None
26
+
27
+
28
+ class YourResult(BaseModel):
29
+ """Result model for your plugin operations.
30
+
31
+ Attributes:
32
+ success: Whether operation succeeded.
33
+ data: Result data.
34
+ error: Error message if failed.
35
+ """
36
+
37
+ success: bool
38
+ data: Optional[YourDataModel] = None
39
+ error: Optional[str] = None
@@ -0,0 +1,48 @@
1
+ """State definitions for your subagent.
2
+
3
+ Define the state that flows through your agent's graph.
4
+ """
5
+
6
+ from typing import Annotated, Any, Optional
7
+
8
+ from langgraph.graph import add_messages
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class YourConfig(BaseModel):
13
+ """Configuration for your plugin.
14
+
15
+ Attributes:
16
+ option1: Configuration option 1.
17
+ option2: Configuration option 2.
18
+ max_items: Maximum number of items to process.
19
+ """
20
+
21
+ option1: str = "default_value"
22
+ option2: int = 10
23
+ max_items: int = 100
24
+
25
+
26
+ class YourState(BaseModel):
27
+ """State for your subagent.
28
+
29
+ This state flows through the agent graph and is updated by nodes.
30
+
31
+ Attributes:
32
+ messages: Conversation messages (for LLM interactions).
33
+ input_data: Input data to process.
34
+ results: Processing results.
35
+ errors: Any errors encountered.
36
+ config: Plugin configuration.
37
+ """
38
+
39
+ # Messages for LLM interactions
40
+ messages: Annotated[list[Any], add_messages] = Field(default_factory=list)
41
+
42
+ # Plugin-specific state
43
+ input_data: Optional[Any] = None
44
+ results: list[Any] = Field(default_factory=list)
45
+ errors: list[str] = Field(default_factory=list)
46
+
47
+ # Configuration
48
+ config: YourConfig = Field(default_factory=YourConfig)