In this section

CapDAG separates the language-neutral cartridge runtime from cartridge-level data contracts. Use CapDAG to build and host a cartridge. Add a CapDAG Cartridge SDK when you need shared LLM payloads, prompt policy, page selection, retry policy, or other shared helpers.

Choose a package

Language Runtime and protocol CapDAG cartridge package
Rust capdag capdag-cartridge-sdk
Go capdag-go capdag-cartridge-sdk-go
Swift / Objective-C capdag-objc (CapDAG and Bifaci products) capdag-cartridge-sdk-objc
Python capdag Runtime and cartridge contracts in one package

The Rust, Go, Swift/Objective-C, Python, and JavaScript CapDAG implementations share URN, definition, dispatch, and planning contracts. Cartridge runtime scaffolds are available for Rust, Go, Swift, and Python; JavaScript currently provides planner and protocol-library parity, not a cartridge scaffold.

Start with Build your first cartridge. The CapDAG reference covers runtime behavior, input and output, peer invocation, and progress and logging.

Python runtime

Install Python 3.11 or newer and the public runtime:

python3 -m pip install capdag

The generated project from capdag new NAME --python is the canonical working example. Its central pattern is:

from capdag.bifaci.cartridge_runtime import Request, WET_KEY_REQUEST
from ops import DryContext, Op, OpMetadata, WetContext

class MyOp(Op):
    async def perform(self, dry: DryContext, wet: WetContext) -> None:
        request: Request = wet.get_required(WET_KEY_REQUEST)
        payload = request.take_input().collect_all_bytes()
        output = request.emitter()
        output.start(False)
        output.finish(1.0, "complete")
        output.emit_cbor(payload.decode("utf-8"))

    def metadata(self) -> OpMetadata:
        return OpMetadata.builder("MyOp").build()

take_input() transfers ownership of the input package and may be called only once. collect_all_bytes() is appropriate for bounded scalar input; use the stream APIs described in the CapDAG input/output reference when data must remain incremental. emitter() returns the response emitter. A handler that delegates work uses request.peer() and must collect peer responses with the appropriate progress-forwarding method when that peer emits logs.

Build the manifest with CapManifest, Cap, CapArg, CapOutput, and declared argument sources. Every manifest includes CAP_IDENTITY; the runtime provides its handler. Build a custom Cap URN with CapUrnBuilder, retain its canonical .to_string(), and register the operation under that exact value:

runtime = CartridgeRuntime.with_manifest(build_manifest())
runtime.register_op_type(CAP_URN, MyOp)
runtime.run()

The runtime constructs a new registered operation for each request. Keep request state in local variables or WetContext, not on a reusable global.

Rust product SDK

capdag-cartridge-sdk re-exports capdag and adds these modules:

Module Contract
llm LLM request, stream, vocabulary, model-information types, Media URNs, Cap URNs, and backend classification.
prompt Prompt-strategy classification and the shared default system prompt.
pages One-based page selection with ordered, deduplicated, clamped ranges.
net_retry Shared bounded retry policy for transient HTTP failures.
structured_queries Structured-query definitions, rendering, registry, and result types.

LLM request

use capdag_cartridge_sdk::llm::LlmGenerationRequest;

let request = LlmGenerationRequest::with_defaults(
    "Summarize this document",
    "hf:Qwen/Qwen2.5-0.5B-Instruct",
);
let json = request.to_json();

prompt and model_spec are required. Optional fields are request_type, system_prompt, request_id, max_tokens, temperature, top_k, top_p, min_p, seed, grammar, json_schema, constraint, chat_template, stop_sequences, max_context_length, batch_size, rope_freq_base, rope_freq_scale, repeat_penalty, and hf_token. A Hugging Face token is needed for a gated repository and is forwarded to the model download.

with_defaults selects generation and sets max_tokens=512, temperature=0.7, top_k=40, top_p=0.9, min_p=0.05, seed=42, max_context_length=0 (automatic sizing), batch_size=2048, rope_freq_base=10000, rope_freq_scale=1, and repeat_penalty=1.1.

ConstraintSpec supports JSON Schema, regular expressions, grammars, and tool calls. RequestType is Generate, GetVocab, or GetInfo; an absent value is treated as Generate.

LLM stream and metadata

The output media type is media:fmt=ndjson;llm-text-stream. Each line is an LlmStreamMessage: Token, Status, Complete, ToolRequest, or Error. to_line() and from_line() serialize and parse one NDJSON record.

LlmVocabResponse carries vocab and optional vocab_size. LlmModelInfo carries required model_spec and vocab_size, plus optional context_length, embedding_dim, file_size_bytes, head_count, layer_count, supports_chat, and supports_tools.

The corresponding media constants are:

  • MEDIA_LLM_GENERATION_REQUEST
  • MEDIA_LLM_TEXT_STREAM
  • MEDIA_LLM_VOCAB_RESPONSE
  • MEDIA_LLM_MODEL_INFO_RESPONSE

The SDK also exports Cap URNs for GGUF, MLX, Candle, constrained inference, vocabulary, model information, embeddings, embedding dimensions, and image description. Use the constants rather than copying their serialized values.

backend_for_model_spec selects MLX for mlx-community or ;mlx, GGUF for a spec containing gguf, and Candle otherwise.

Shared policies

parse_index_range accepts one-based N, A-B, and A- segments separated by commas. It preserves written order, drops later duplicates, clamps an end past the document, and rejects zero, backward ranges, malformed segments, empty documents, and ranges that start after the last page. Returned indices are zero-based. An absent or empty selection returns every page.

The default network retry policy makes four total attempts, starts at 250 ms, caps exponential backoff at 10 seconds, and honors numeric Retry-After for at most 30 seconds. The download policy makes five attempts with a 500 ms base, 30-second backoff cap, and 60-second Retry-After cap. Retryable HTTP statuses are 408, 425, 429, 500, 502, 503, and 504; timeouts, connection failures, DNS failures, and request-send errors are also transient. Authentication failures, 404s, other permanent responses, and the final failed attempt are returned to the caller unchanged.

Prompt classification chooses chat templating only for chat-template-jinja and chat-template-short. An absent or unknown template uses raw completion text and drops any system prompt. The backend cartridge owns template rendering; the SDK owns the shared decision.

Go product SDK

Install the module and import llm:

go get github.com/machinefabric/capdag-cartridge-sdk-go
import "github.com/machinefabric/capdag-cartridge-sdk-go/llm"

request := llm.NewGenerationRequestWithDefaults(
    "Summarize this document",
    "hf:Qwen/Qwen2.5-0.5B-Instruct",
)
body := request.ToJSON()

The Go package mirrors the Rust LLM request, constraint, tool, stream, vocabulary, model-information, media-constant, Cap-constant, and backend classification contracts. Optional request values use pointers. Parse a stream record with llm.FromLine; serialize one with ToLine. The Rust SDK defines these CapDAG-specific wire types.

Swift and Objective-C product SDK

Add capdag-cartridge-sdk-objc as a Swift package and import the product you need:

import CapDAGCartridgeSDK
import CapDAGCartridgeSDKSwift

CapDAGCartridgeSDK depends on the CapDAG and Bifaci products from capdag-objc. CSCartridgeCaps is an ordered, secure-codable collection of CSCap values. addCap(_:) appends a capability, caps returns the ordered values, and capUrns() returns their URN strings. Parse those strings with CSCapUrn; do not route them by raw string comparison.

The Swift-only product mirrors RefinedDims, PromptStrategy, classifyPrompt, and DEFAULT_SYSTEM_PROMPT from the Rust prompt module.

URN rules

Cap and Media URNs are Tagged URNs, not ad hoc MIME strings. Quotes around direction values are part of a canonical Cap URN, and tag order is normalized. Construct and parse URNs with the CapDAG APIs. See the normative Cap URN structure and Media URNs references.