Extension Points in semantica.core for Custom Plugins: A Developer's Guide

The semantica.core package exposes three primary extension points—PluginRegistry, MethodRegistry, and ConfigManager—that enable dynamic discovery, registration, and configuration of custom plugins without modifying framework internals.

The semantica-agi/semantica framework is designed around a modular architecture that separates core orchestration logic from extensible components. Developers can augment knowledge-graph pipelines, ingestion workflows, and processing steps through well-defined APIs in the semantica.core module. These extension points provide version-aware lifecycle management, dependency resolution, and environment-specific configuration handling.

PluginRegistry: Dynamic Plugin Discovery and Lifecycle Management

The PluginRegistry class in semantica/core/plugin_registry.py serves as the primary mechanism for managing custom ingestors, parsers, and exporters. It handles discovery, versioning, dependency resolution, and instance caching.

Directory-Based Discovery

At initialization, PluginRegistry walks the directories specified in the plugin_paths parameter to build a catalog of available plugins. This auto-discovery mechanism scans for plugin classes without requiring manual registration.

from semantica.core import PluginRegistry

# Auto-discover plugins under ./plugins

registry = PluginRegistry(plugin_paths=["./plugins"])

Manual Registration and Metadata

For explicit control, use register_plugin() to bind a class to a specific name along with version and capability metadata. The system uses the PluginInfo dataclass to track dependencies and features.

class MyIngestor:
    def initialize(self):
        print("MyIngestor initialized")
    
    def execute(self, data):
        return {"ingested": True, "data": data}

registry.register_plugin(
    "my_ingestor",
    MyIngestor,
    version="0.1.0",
    description="Demo ingestor for CSV files",
    capabilities=["ingest"],
)

Loading and Initialization

The load_plugin(name, **config) method resolves dependencies, instantiates the plugin, invokes its initialize() method, and caches the instance for reuse.

plugin = registry.load_plugin("my_ingestor")
result = plugin.execute({"file": "sample.csv"})

MethodRegistry: Custom Orchestration Methods

The MethodRegistry (exposed as the global method_registry in semantica/core/registry.py) allows binding custom Python callables to named tasks. This enables user-defined pipeline steps that the high-level Semantica orchestrator can invoke by name.

Registering Task-Specific Methods

Use method_registry.register(task, name, func) to associate a function with a specific task category. Valid tasks include "knowledge_base", "pipeline", or custom categories.

from semantica.core.registry import method_registry

def fast_kb_builder(sources, **kwargs):
    print("Running fast builder on:", sources)
    return {"status": "quick-kb-built"}

method_registry.register("knowledge_base", "fast", fast_kb_builder)

Runtime Method Resolution

Retrieve and execute registered methods using method_registry.get(task, name). This lookup mechanism decouples orchestration logic from implementation details.

builder = method_registry.get("knowledge_base", "fast")
builder(["doc1.pdf", "doc2.pdf"])

ConfigManager: Centralized Configuration with Plugin Hooks

The ConfigManager in semantica/core/config_manager.py provides deep-merge configuration loading with validation and environment-variable overrides. Plugins can read custom configuration sections through this centralized system.

Loading and Accessing Configuration

Use load_from_file() to parse YAML configurations, then access plugin-specific sections via the config object. The manager automatically processes SEMANTICA_ prefixed environment variables.

from semantica.core import ConfigManager

cfg = ConfigManager()
config = cfg.load_from_file("config.yaml")

# Access nested plugin configuration

option_value = config.get("my_plugin.option_a")

Integration with Plugin Initialization

When loading plugins, pass configuration data to the plugin constructor through the load_plugin() method's keyword arguments.

registry = PluginRegistry(plugin_paths=["./plugins"])
my_plugin = registry.load_plugin("my_plugin")

# Configuration is available via the plugin instance

print(my_plugin.config)

Key Implementation Files

Understanding the source structure helps when debugging or extending these APIs:

Summary

  • PluginRegistry provides dynamic discovery, manual registration, version-aware loading, and lifecycle management for custom components.
  • MethodRegistry enables task-based registration of custom orchestration methods accessible by name throughout the framework.
  • ConfigManager offers deep-merge configuration loading with environment variable support, allowing plugins to define custom settings sections.
  • All extension points support dependency resolution and instance caching to optimize runtime performance.
  • Source implementations reside in semantica/core/plugin_registry.py, semantica/core/registry.py, and semantica/core/config_manager.py.

Frequently Asked Questions

How does auto-discovery differ from manual plugin registration?

Auto-discovery scans plugin_paths directories at PluginRegistry initialization to find plugin classes automatically. Manual registration via register_plugin() gives explicit control over names, versions, and capabilities. Both methods result in the same internal metadata stored in PluginInfo dataclasses, but manual registration allows you to override discovered plugins or register classes outside the standard directory structure.

What is the difference between PluginRegistry and MethodRegistry?

PluginRegistry manages full plugin classes with lifecycle methods like initialize() and execute(), handling instantiation and dependency resolution. MethodRegistry manages individual Python functions bound to task names for orchestration purposes. Use PluginRegistry for complex components requiring state and configuration; use MethodRegistry for lightweight processing functions that the orchestrator calls by name.

Can plugins access their own configuration sections automatically?

Yes. When loading a plugin via registry.load_plugin("name"), the ConfigManager can pass plugin-specific configuration to the plugin constructor. Plugins receive their configuration section through the instance's config attribute, allowing them to read settings defined in YAML files or overridden via SEMANTICA_ environment variables without directly accessing the global configuration object.

How does version management work for custom plugins?

The register_plugin() method accepts a version parameter stored in the PluginInfo metadata. The registry uses this information for dependency resolution when plugins declare dependencies on other plugins. While the current implementation primarily tracks versions for compatibility checking, future framework updates may support loading specific version ranges or enforcing semantic versioning constraints during the resolution phase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →