install
source · Clone the upstream repo
git clone https://github.com/kreuzberg-dev/kreuzberg
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/kreuzberg-dev/kreuzberg "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.ai-rulez/skills/plugin-architecture-patterns" ~/.claude/skills/kreuzberg-dev-kreuzberg-plugin-architecture-patterns && rm -rf "$T"
manifest:
.ai-rulez/skills/plugin-architecture-patterns/SKILL.mdsource content
priority: critical
Plugin Architecture & Registration
Plugin Types
| Type | Trait | Location |
|---|---|---|
| Document Extractor | | |
| OCR Backend | | |
| Post Processor | | |
| Validator | | |
DocumentExtractor Implementation
use crate::plugins::{DocumentExtractor, Plugin}; use async_trait::async_trait; pub struct MyExtractor; impl Plugin for MyExtractor { fn name(&self) -> &str { "my-extractor" } fn version(&self) -> String { env!("CARGO_PKG_VERSION").to_string() } } #[async_trait] impl DocumentExtractor for MyExtractor { async fn extract_bytes(&self, content: &[u8], mime_type: &str, config: &ExtractionConfig) -> Result<ExtractionResult> { /* ... */ } fn supported_mime_types(&self) -> &[&str] { &["application/x-custom"] } fn priority(&self) -> i32 { 50 } // WASM support (optional) fn as_sync_extractor(&self) -> Option<&dyn SyncExtractor> { None } }
Priority System
| Range | Use |
|---|---|
| 0-25 | Fallback/low-quality |
| 26-49 | Alternative extractors |
| 50 | Default (built-in) |
| 51-75 | Premium/enhanced |
| 76-100 | Specialized/high-priority |
Registry selects highest priority extractor for each MIME type. Override built-ins with priority > 50.
Registration
// In extractors/mod.rs → register_default_extractors() let registry = get_document_extractor_registry(); let mut registry = registry.write() .map_err(|e| KreuzbergError::Other(format!("Registry lock poisoned: {}", e)))?; registry.register(Arc::new(MyExtractor::new()))?;
Feature-Gated Registration
#[cfg(feature = "office")] { registry.register(Arc::new(DocxExtractor::new()))?; registry.register(Arc::new(PptxExtractor::new()))?; }
PostProcessor Pattern
impl PostProcessor for MyProcessor { async fn process(&self, result: &mut ExtractionResult, config: &ExtractionConfig) -> Result<()> { result.content = process_content(&result.content); Ok(()) } fn stage(&self) -> ProcessorStage { ProcessorStage::Middle } }
Stages:
Early → Middle → Late. Failures isolated (don't block others).
Critical Rules
- All plugins MUST be
Send + Sync - Feature gate with
for optional formats#[cfg(feature = "...")] - Use
for#[async_trait]DocumentExtractor - Initialization via
(lazy, called before first extraction)ensure_initialized() - Plugin names: kebab-case (e.g.,
)"pdf-extractor"