Creating Plugins for GeneForgeLang
This guide explains how to create custom plugins for GeneForgeLang using the cookiecutter template.
Overview
GeneForgeLang plugins extend the platform's functionality by integrating external tools and services. Plugins can be generators, optimizers, or general-purpose tools that process data within GFL workflows.
Prerequisites
- Python 3.9 or higher
- GeneForgeLang installed
- Basic understanding of Python programming
- Familiarity with the GFL plugin architecture
Using the Cookiecutter Template
The easiest way to create a new plugin is to use the official cookiecutter template:
# Install cookiecutter if you haven't already
pip install cookiecutter
# Create a new plugin using the template
cookiecutter https://github.com/Fundacion-de-Neurociencias/cookiecutter-gfl-plugin.git
The template will prompt you for information about your plugin:
- Plugin name
- Plugin description
- Plugin type (generator, optimizer, or general)
- Author information
- Python version requirements
Plugin Structure
A typical GFL plugin has the following structure:
my-gfl-plugin/
├── pyproject.toml # Project configuration
├── README.md # Plugin documentation
├── my_plugin/ # Plugin source code
│ ├── __init__.py # Package initialization
│ ├── plugin.py # Main plugin implementation
│ └── utils.py # Utility functions
├── tests/ # Test files
│ ├── __init__.py
│ ├── test_plugin.py # Plugin tests
│ └── fixtures/ # Test data
└── examples/ # Example workflows
└── example.gfl # Example GFL workflow
Plugin Types
Generator Plugins
Generator plugins create new biological entities such as proteins, DNA sequences, or molecules.
from gfl.plugins import GeneratorPlugin, DesignCandidate
from typing import List, Dict, Any
class MyGenerator(GeneratorPlugin):
@property
def name(self) -> str:
return "my-generator"
@property
def version(self) -> str:
return "1.0.0"
def generate(
self,
entity: str,
objective: Dict[str, Any],
constraints: List[str],
count: int,
**kwargs
) -> List[DesignCandidate]:
# Implementation here
pass
Optimizer Plugins
Optimizer plugins perform intelligent parameter space exploration and optimization.
from gfl.plugins import OptimizerPlugin, OptimizationStep
from typing import List, Dict, Any
class MyOptimizer(OptimizerPlugin):
@property
def name(self) -> str:
return "my-optimizer"
@property
def version(self) -> str:
return "1.0.0"
def setup(
self,
search_space: Dict[str, str],
strategy: Dict[str, Any],
objective: Dict[str, Any],
budget: Dict[str, Any]
) -> None:
# Implementation here
pass
def suggest_next(self, experiment_history: List[Any]) -> OptimizationStep:
# Implementation here
pass
General Plugins
General plugins can perform any operation within a GFL workflow.
from gfl.plugins import Plugin
from typing import Dict, Any
class MyPlugin(Plugin):
@property
def name(self) -> str:
return "my-plugin"
@property
def version(self) -> str:
return "1.0.0"
def run(self, input_data: Any, params: Dict[str, Any]) -> Any:
# Implementation here
pass
Entry Points
To make your plugin discoverable by GFL, you need to register it as an entry point in your pyproject.toml:
[project.entry-points."gfl.plugins"]
my-plugin = "my_plugin.plugin:MyPlugin"
Configuration
Plugins can accept configuration parameters:
class MyPlugin(Plugin):
def __init__(self, config: Dict[str, Any] = None):
super().__init__(config)
self.config = config or {}
self.some_parameter = self.config.get("some_parameter", "default_value")
Testing Your Plugin
Write comprehensive tests for your plugin:
import pytest
from my_plugin.plugin import MyPlugin
def test_plugin_initialization():
plugin = MyPlugin()
assert plugin.name == "my-plugin"
assert plugin.version == "1.0.0"
def test_plugin_functionality():
plugin = MyPlugin()
result = plugin.run("input_data", {"param": "value"})
# Add assertions based on expected behavior
Publishing Your Plugin
-
Create a source distribution:
bash python -m build -
Upload to PyPI:
bash twine upload dist/* -
Users can then install your plugin:
bash pip install my-gfl-plugin
Best Practices
- Error Handling: Implement proper error handling and validation
- Documentation: Provide clear documentation and examples
- Testing: Write comprehensive tests with good coverage
- Performance: Optimize for performance, especially for computationally intensive operations
- Dependencies: Minimize external dependencies and specify version constraints
- Logging: Use appropriate logging levels for debugging and monitoring
Example Plugin
Here's a complete example of a simple plugin:
from gfl.plugins import Plugin
from typing import Dict, Any
class SequenceAnalyzer(Plugin):
@property
def name(self) -> str:
return "sequence-analyzer"
@property
def version(self) -> str:
return "1.0.0"
def run(self, input_data: str, params: Dict[str, Any]) -> Dict[str, Any]:
sequence = input_data
analysis = {
"length": len(sequence),
"gc_content": self._calculate_gc_content(sequence),
"is_valid_dna": self._validate_dna(sequence)
}
return {"analysis": analysis}
def _calculate_gc_content(self, sequence: str) -> float:
gc_count = sequence.upper().count('G') + sequence.upper().count('C')
return (gc_count / len(sequence)) * 100 if sequence else 0
def _validate_dna(self, sequence: str) -> bool:
valid_bases = set('ACGTN')
return all(base in valid_bases for base in sequence.upper())
Getting Help
If you need help creating plugins:
- Check the Plugin Ecosystem Documentation
- Review existing plugins in the repository
- Join the community discussions
- Open an issue on GitHub for specific questions