In this section

Document processing cartridges: PDF, text, and the standard cap patterns they use.

Content cartridge architecture

Content cartridges are simpler than model cartridges:

  • No peer calls; all processing is self-contained.
  • No model loading or keepalive; operations complete quickly.
  • Shorter handler implementations, typically under 100 lines each.
  • Use standard cap URN helpers from capdag::standard::caps.

The handler pattern is: receive input → process → emit output. No three-phase architecture, no FFI thread management.

Source: pdfcartridge, txtcartridge.

Standard caps

graph LR
    PDF["media:document;ext=pdf"] -->|generate_thumbnail| IMG["media:ext=png;image"]
    PDF -->|extract_metadata| REC["media:record"]
    PDF -->|extract_outline| JSON["media:json"]
    PDF -->|disbind| LIST["media:document;ext=pdf;list"]

    TXT["media:enc=utf-8"] -->|generate_thumbnail| IMG2["media:ext=png;image"]
    TXT -->|extract_metadata| REC2["media:record"]
    MD["media:enc=utf-8;ext=md"] -->|extract_outline| JSON2["media:fmt=json;record"]

capdag::standard::caps defines cap URN helpers for common document operations:

Function Operation Description
generate_thumbnail_urn(media) generate;target=thumbnail Render a visual preview of the input.
extract_metadata_urn(media) extract;target=metadata Extract structured metadata (title, author, page count, dates).
extract_outline_urn(media) extract;target=outline Extract document outline or table of contents.
disbind_urn(media) op=disbind Extract individual pages or sections as separate items.

Each function takes a media URN (e.g., "media:document;ext=pdf") and returns the cap URN string for that operation on that media type. The cap URN includes the appropriate in= and out= specs.

Source: capdag-rs/src/standard/caps.rs.

pdfcartridge

Provides four caps for PDF documents:

generate_thumbnail

Renders PDF pages as PNG thumbnails. Arguments:

  • media:document;ext=pdf (required, stdin): The PDF document.
  • media:width;enc=utf-8;numeric (optional): Output width in pixels.
  • media:height;enc=utf-8;numeric (optional): Output height in pixels.
  • media:index-range;enc=utf-8 (optional): Which pages to render (e.g., “0-2” for first three pages).

extract_metadata

Extracts PDF metadata as JSON. Returns a record with title, author, subject, creator, creation date, modification date, page count, and other document properties.

Arguments: media:document;ext=pdf (required, stdin).

extract_outline

Extracts the PDF outline (bookmarks/table of contents) as a hierarchical JSON structure. Arguments:

  • media:document;ext=pdf (required, stdin).
  • media:max-depth;enc=utf-8;numeric (optional): Maximum depth of outline traversal.
  • media:include-order-indexes;enc=utf-8 (optional): Whether to include ordering indexes.

disbind

Extracts individual PDF pages as separate items. Uses emit_list_item() to stream pages one at a time:

output.start(true, input_meta.clone())?;
for (i, page_bytes) in pages.iter().enumerate() {
    output.progress(i as f32 / pages.len() as f32, &format!("Page {}", i + 1));
    output.emit_list_item(&ciborium::Value::Bytes(page_bytes.to_vec()), None).await?;
}

Source: pdfcartridge/src/main.rs.

txtcartridge

Provides thumbnail, metadata, and outline extraction for text-based formats: plain text (.txt), reStructuredText (.rst), log files (.log), and Markdown (.md).

The same handler implementations are registered for all four media types. Outline extraction is available only for structured text formats (Markdown, reStructuredText) where headings define the document structure.

Multi-type handler pattern

A single handler serves multiple media types by trying each known URN:

fn require_text_content(streams: &[(String, Vec<u8>)]) -> Result<&[u8], StreamError> {
    for urn in &[MEDIA_TXT, MEDIA_RST, MEDIA_LOG, MEDIA_MD] {
        if let Some(bytes) = find_stream(streams, urn) {
            return Ok(bytes);
        }
    }
    Err(StreamError::Protocol("No recognized text stream found".to_string()))
}

Registration loops over all media types:

for media in &[MEDIA_TXT, MEDIA_RST, MEDIA_LOG, MEDIA_MD] {
    runtime.register_op(&generate_thumbnail_urn(media), || Box::new(ThumbnailOp));
    runtime.register_op(&extract_metadata_urn(media), || Box::new(MetadataOp));
}

// Outline only for structured formats
for media in &[MEDIA_MD, MEDIA_RST] {
    runtime.register_op(&extract_outline_urn(media), || Box::new(OutlineOp));
}

Cap URN dispatch (see Dispatch) ensures that the incoming request matches exactly one of the registered media types. The handler’s fallback loop over URNs finds the matching stream.

Source: txtcartridge/src/main.rs.

Building a content cartridge

To add a new content cartridge for a hypothetical media type (e.g., EPUB):

  1. Create the crate: Set up a Rust binary crate with #[tokio::main] and dependencies on capdag and ops.

  2. Define cap URNs: Use standard helpers or build custom ones:
    let thumb_cap = make_cap(generate_thumbnail_urn("media:document;ext=epub"));
    let meta_cap = make_cap(extract_metadata_urn("media:document;ext=epub"));
    
  3. Build the manifest: Include identity_cap() and all your caps:
    let manifest = CapManifest::new(
        "epubcartridge".to_string(),
        "1.0.0".to_string(),
        "EPUB document processing".to_string(),
        vec![identity_cap(), thumb_cap, meta_cap],
    );
    
  4. Implement handlers: Each handler implements Op<()>. See Handler patterns for the standard patterns.

  5. Register and run:
    let mut runtime = CartridgeRuntime::with_manifest(manifest);
    runtime.register_op(&generate_thumbnail_urn("media:document;ext=epub"), || Box::new(ThumbnailOp));
    runtime.register_op(&extract_metadata_urn("media:document;ext=epub"), || Box::new(MetadataOp));
    runtime.run().await?;
    

Use pdfcartridge as a reference implementation; it demonstrates all four standard operations with clean handler code.