# Create plugin metadata
meta = PluginMeta(
name="example_plugin",
version="1.0.0",
description="An example plugin",
category="transcription",
interface="cjm_transcription_plugin_system.plugin_interface.TranscriptionPlugin",
config_schema={
"type": "object",
"properties": {
"model": {"type": "string", "default": "base"},
"device": {"type": "string", "enum": ["cpu", "cuda"]}
}
}
)
print("PluginMeta instance:")
print(meta)
print(f"\nName: {meta.name}")
print(f"Version: {meta.version}")
print(f"Category: {meta.category}")
print(f"Interface: {meta.interface}")
print(f"Config Schema: {meta.config_schema}")
print(f"Enabled: {meta.enabled}")
print(f"Instance: {meta.instance}")Plugin Metadata
PluginMeta
The PluginMeta dataclass stores metadata about a plugin, including its name, version, and runtime state.
ResourceRequirements
def ResourceRequirements(
requires_gpu:bool=False, platforms:List=<factory>, accelerators:List=<factory>
)->None:
Binary hard-facts about what a plugin needs to run (Phase 5a).
Quantitative resource amounts (min_vram_mb, etc.) deliberately omitted per CR-7’s reactive resource management reframing — plugin authors can’t reliably estimate model × dtype × quantization combinatorics, and Blender- style variable-render plugins can’t estimate at all. The substrate uses these binary hard-facts purely for discovery filtering; actual resource contention is handled reactively by CR-7’s eviction + retry flow.
requires_gpu: True if the plugin needs any GPU; the substrate gates execution on a system monitor reporting one is present.platforms: e.g., [“linux-x64”, “darwin-arm64”]. Empty list means no platform constraint declared (assume universal compatibility).accelerators: e.g., [“cuda”, “mps”, “cpu”]. Informational; substrate doesn’t auto-select but consumers can filter on the values.
PluginTaxonomy
def PluginTaxonomy(
domain:str, role:str, interface_fqcn:str
)->None:
Derived classification of a plugin via its interface FQN.
Populated at install time by _generate_manifest’s introspection script (running in the plugin’s own conda env, where the interface library is installed). Substrate stores strings only — no host-side imports of interface libraries needed for taxonomy queries.
domain: the substrate area, derived from the interface library’s naming conventioncjm-<domain>-plugin-system(e.g., “transcription”, “graph”, “media”, “text”).role: the interface class name segment of the FQN (e.g., “TranscriptionPlugin”, “GraphPlugin”, “ForcedAlignmentPlugin”). Multiple plugins can share a role within a domain.interface_fqcn: the full dotted path, kept verbatim for queries and reverse-lookup.
PluginMeta
def PluginMeta(
name:str, version:str, description:str='', category:str='', interface:str='', taxonomy:Optional=None,
resources:Optional=None, config_schema:Optional=None, instance:Optional=None, enabled:bool=True,
last_executed:float=0.0, config_schema_drift:bool=False, live_config_schema:Optional=None
)->None:
Metadata about a plugin.
PluginInstance (CR-10)
CR-10 introduces multi-instance plugin support: a single discovered plugin can have multiple loaded instances differing by configuration. PluginInstance is the per-instance runtime state, keyed by instance_id in PluginManager._instances / PluginManager.instances.
The PluginMeta continues to hold per-plugin (per-name) discovery + canonical-instance state; PluginInstance covers what differs across multiple loads of the same plugin: the proxy, the config that initialized it, the enabled flag, last-executed timestamp, and creation time.
Naming conventions (constrained-pattern, per CR-10 Q-resolution):
- Default:
instance_id == plugin_name— backward-compat for single-instance code paths. - Named: caller passes a constrained string (
{alphanumeric, _, -}+, len ≤ 64). - Auto-generated: caller passes
new_instance=Trueand the substrate produces{plugin_name}-{6-char hex}.
The first instance loaded for a plugin (default instance_id == plugin_name) is the “canonical” instance — code reading PluginMeta.instance continues to see it. Multi-instance loads after the first don’t disturb that canonical reference.
PluginInstance
def PluginInstance(
instance_id:str, plugin_name:str, config:Dict=<factory>, proxy:Optional=None, enabled:bool=True,
last_executed:float=0.0, created_at:datetime=<factory>, config_hash:str='',
max_concurrent_requests:Optional=None
)->None:
Per-instance runtime state for a loaded plugin (CR-10 multi-instance).
Differs from PluginMeta in scope: - PluginMeta is per-plugin-name discovery + canonical-instance state. - PluginInstance is per-load-call runtime state.
A plugin loaded with no instance_id (default) gets instance_id == plugin_name and is the canonical instance referenced by PluginMeta.instance. Multi-instance loads (instance_id != plugin_name) add entries to PluginManager.instances without changing the canonical reference.
PluginLoadSpec
def PluginLoadSpec(
meta:Any, config:Optional=None, instance_id:Optional=None, new_instance:bool=False
)->None:
One entry in PluginManager.load_plugins_concurrent’s batch input (CR-10).
Mirrors the positional arguments of load_plugin so the concurrent helper can fan out load calls without repeating the per-spec instance_id / new_instance plumbing.
meta: the discovered PluginMeta to load (must have a.manifestattached).config: initial configuration; falls through to persisted-or-schema-defaults when None (default-instance only; multi-instance starts fresh).instance_id: explicit instance_id (validated against [A-Za-z0-9_-]{1,64}). None defaults to plugin_name (single-instance backward compat).new_instance: when True with instance_id=None, auto-generate{plugin_name}-{6-hex}.
Example: Creating Plugin Metadata
# Test with minimal arguments
minimal_meta = PluginMeta(name="minimal", version="0.1.0")
print(f"Minimal PluginMeta: {minimal_meta}")
# Test equality
meta_copy = PluginMeta(name="minimal", version="0.1.0")
print(f"\nEquality test: {minimal_meta == meta_copy}")Minimal PluginMeta: PluginMeta(name='minimal', version='0.1.0', description='', author='', package_name='', category='', interface='', config_schema=None, instance=None, enabled=True, last_executed=0.0)
Equality test: True
# CR-1 + Phase 5a: PluginTaxonomy and ResourceRequirements round-trip cleanly,
# and PluginMeta accepts either or both as optional fields.
tax = PluginTaxonomy(
domain="transcription",
role="TranscriptionPlugin",
interface_fqcn="cjm_transcription_plugin_system.plugin_interface.TranscriptionPlugin",
)
assert tax.domain == "transcription"
assert tax.role == "TranscriptionPlugin"
res = ResourceRequirements(
requires_gpu=True,
platforms=["linux-x64", "darwin-arm64"],
accelerators=["cuda", "mps"],
)
assert res.requires_gpu is True
assert "linux-x64" in res.platforms
# ResourceRequirements defaults: no GPU, empty platforms (universal), empty accelerators.
default_res = ResourceRequirements()
assert default_res.requires_gpu is False
assert default_res.platforms == []
assert default_res.accelerators == []
# PluginMeta accepts taxonomy + resources; both default to None for legacy manifests.
typed_meta = PluginMeta(
name="whisper-local",
version="1.0.0",
description="Local Whisper-based STT",
interface="cjm_transcription_plugin_system.plugin_interface.TranscriptionPlugin",
taxonomy=tax,
resources=res,
)
assert typed_meta.taxonomy.role == "TranscriptionPlugin"
assert typed_meta.resources.requires_gpu is True
legacy_meta = PluginMeta(name="legacy", version="0.0.1")
assert legacy_meta.taxonomy is None
assert legacy_meta.resources is None
print("✓ PluginTaxonomy + ResourceRequirements + PluginMeta integration")