RFC 0012: Provider Requirements
- Status: Implemented
- Author(s): Charlie Holland (@chaholl)
- Created: 2026-06-25
- Updated: 2026-06-25
- Related Issues: TBD (deploy provider-binding ergonomics)
Summary
A promptpack can only run if the host runtime supplies the model providers it depends on — a primary LLM, often an embedding model for retrieval, sometimes a judge model for evals. Today a pack never declares these needs, so every runtime and every deployment rediscovers them by hand and fails late when a binding is missing, mistyped, or points at the wrong kind of model. This RFC adds an optional, runtime-agnostic requires.providers block: a pack declares the logical providers it needs — by key, role, and a human-readable description — and each runtime resolves those requirements to its own concrete providers. The block is optional (fully backward compatible), but when present it is validated strictly.
Motivation
Promptpacks are portable, but their provider needs are implicit. A pack is tested against concrete models (tested_models), yet nothing in the pack states what it needs to run in machine-readable form. The gap shows up most painfully at deployment, where a logical need must be mapped to a concrete provider with no contract to check against:
- Wrong or missing bindings fail late. A deployer hand-maps "the LLM" to a provider name; a typo or a non-existent provider only surfaces at first request, not at validation time.
- No coverage check. A pack that needs an embedding provider (RAG) deploys "healthy" and then fails when retrieval runs, because nothing declared the dependency.
- No test/deploy parity. A pack tested on
anthropic/claude-haikucan be deployed againstopenai/gpt-4osilently — thetested_modelsprovenance exists, but there is no requirement to compare a resolved provider against. - No basis for automation. Tooling cannot auto-bind, scaffold, or warn without knowing the pack's logical needs.
Declaring requirements once, in the portable pack, lets every runtime and deployer validate coverage, resolve bindings, auto-configure, and warn on divergence — instead of re-deriving the same information by hand.
Goals
- Let a pack declare, runtime-agnostically, the model providers it requires to run.
- Make requirements human-legible (a
description) so operators can wire up the right capabilities. - Give runtimes and deployers a machine-readable contract for coverage checks, resolution/auto-binding, and test/deploy parity.
- Be fully backward compatible: optional block, strict validation only when present.
Non-Goals
- Defining concrete provider configuration — endpoints, API keys, model identifiers, regions. Those are runtime/deployment concerns, not part of the portable pack.
- Mandating an inference algorithm. Requirements are authored; a runtime may infer or pre-populate them as a convenience, but the spec does not require or define inference.
- A full provider-capability taxonomy. This RFC adds a small, optional
capabilitiesset (modalities, context window, tool-use, structured output, embedding dimensions); broader concerns (pricing, region/residency, version pinning) are left to a future RFC (see Future Considerations).
Detailed Design
Core Concept
A provider requirement is a logical dependency on a model provider, identified by:
key— the logical name the pack/runtime resolves the provider by (e.g.default,embeddings,judge).role— the kind of model required (llm,embedding,tts,stt,image, …).description(optional) — a human-readable explanation of what the provider is for and which capabilities it should have.required(optional, defaulttrue) — whether the pack can run at all without it.capabilities(optional) — structured, advisory capabilities (modalities, minimum context window, tool-use, structured output, embedding dimensions) for automatic matching; thedescriptionremains the primary human guidance.
A requirement declares what the pack needs, never which concrete provider satisfies it — resolution is the host runtime's job.
default is a reserved key meaning "the primary LLM": a pack need not declare one, but if it has a primary model it SHOULD use key: default, and runtimes SHOULD treat default as primary (matching existing runtime conventions for "the agent's main model"). key is the sole discriminator between requirements — two models of the same role (e.g. a fast and a strong llm) are simply two requirements with different keys, distinguished for humans by description and for machines by capabilities.
This is deliberately distinct from RFC 0003's variable binding (kind: context|session|env|header), which populates template variables from per-request context. A provider requirement is a pack-level runtime dependency, not a template-variable source.
Schema Changes
New Top-Level Field: requires
{
"requires": {
"type": "object",
"description": "External resources the pack needs to run. Optional; when present, validated strictly. Reserved for future requirement categories (e.g. tools, skills).",
"additionalProperties": false,
"properties": {
"providers": {
"type": "array",
"description": "Logical model-provider requirements. Each entry is a string shorthand (an 'llm' requirement with that key) or a ProviderRequirement object.",
"items": { "$ref": "#/$defs/ProviderRequirement" }
}
}
}
}
New Definition: ProviderRequirement
{
"ProviderRequirement": {
"oneOf": [
{
"type": "string",
"description": "Shorthand: the requirement key. Expands to an llm requirement: { key: <string>, role: \"llm\", required: true }. E.g. \"default\"."
},
{
"type": "object",
"additionalProperties": false,
"required": ["key", "role"],
"properties": {
"key": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]+$",
"description": "Logical name the runtime resolves this provider by (e.g. 'default', 'embeddings', 'judge')."
},
"role": {
"type": "string",
"description": "The kind of model required. An OPEN set: runtimes MAY recognise additional roles. Suggested values (PromptKit's provider roles): 'llm', 'embedding', 'tts', 'stt', 'image', 'inference'.",
"examples": ["llm", "embedding", "tts", "stt", "image", "inference"]
},
"required": {
"type": "boolean",
"default": true,
"description": "Whether the pack cannot run without this provider. Optional requirements degrade features rather than blocking startup."
},
"description": {
"type": "string",
"description": "Human-readable explanation of the provider's purpose and the capabilities it should have, e.g. 'Primary chat model; needs strong tool-use and >=32k context.'"
},
"capabilities": { "$ref": "#/$defs/ProviderCapabilities" }
}
}
]
}
}
New Definition: ProviderCapabilities
Optional, structured, advisory capabilities the satisfying provider should have. The human description remains the primary guidance; capabilities lets runtimes match and warn automatically. The modalities vocabulary deliberately reuses the media-type set from RFC 0004 (MediaConfig.supported_types) rather than inventing a parallel one.
Crucially, the object is open. The fields below are the spec's well-known capabilities, validated when present — but they can't anticipate every provider. A role: inference provider, in particular, may expose capabilities the spec never imagined, so additional keys are allowed and carry no fixed shape. Custom keys SHOULD be namespaced (an x- prefix or a vendor scope) so they never collide with capabilities the spec may standardise later.
{
"ProviderCapabilities": {
"type": "object",
"additionalProperties": true,
"description": "Structured, advisory capabilities the satisfying provider should have. The well-known fields below are validated when present, but the object is OPEN: provider- or role-specific capabilities (a 'role: inference' provider may expose anything) may be added as extra keys with any shape. Custom keys SHOULD be namespaced (e.g. 'x-' prefix or vendor scope) to avoid clashing with fields the spec may define later. All listed fields are optional.",
"properties": {
"modalities": {
"type": "array",
"description": "Media types the provider must handle. Reuses the media-type vocabulary (see RFC 0004 MediaConfig.supported_types). Common: 'text', 'image', 'audio', 'video', 'document'.",
"items": { "type": "string", "pattern": "^[a-z0-9_]+$" }
},
"min_context_tokens": {
"type": "integer",
"minimum": 1,
"description": "Minimum context window, in tokens, the provider must support."
},
"tool_use": {
"type": "boolean",
"description": "Whether the provider must support tool/function calling."
},
"structured_output": {
"type": "boolean",
"description": "Whether the provider must support structured/JSON output."
},
"embedding_dimensions": {
"type": "integer",
"minimum": 1,
"description": "Required embedding vector dimensionality (for role 'embedding')."
}
}
}
}
Why requires rather than a top-level providers: a top-level providers reads as concrete provider definitions; requires unambiguously denotes dependencies the host must satisfy, and leaves room for future requires.tools / requires.skills without another top-level key.
Specification Impact
- New optional root property
requires, alongsidetools,skills,evals,workflow,agents. The rootrequiredlist is unchanged. - Relationship to
tested_models.tested_modelsrecords what a prompt was tested against (provenance);requires.providersrecords what the pack needs (contract). They are complementary and independent — neither implies the other. A runtime MAY cross-check the provider it resolves for a requirement againsttested_modelsto warn on test/deploy divergence (parity). - The
defaultkey is documented as the conventional primary LLM.
Validation Rules
requiresis optional; when present,additionalProperties: false.requires.providersis an optional array; each item is a string or aProviderRequirementobject.- Object form:
keyandroleare required;keymatches^[a-zA-Z0-9_-]+$;additionalProperties: false. - String form expands to
{ "key": <string>, "role": "llm", "required": true }. keyvalues withinrequires.providersMUST be unique (keyis the sole discriminator).requireddefaults totruewhen omitted.capabilities, when present, is an open object (additionalProperties: true): the well-known fields are validated when present, and additional provider/role-specific keys are allowed with any shape. Custom keys SHOULD be namespaced (x-/vendor) to avoid clashing with future spec fields. Every capability is optional and advisory (a hint for matching, not a hard constraint the spec enforces).defaultis a reserved key denoting the primary LLM; a pack is not required to declare it.roleis an open string; validators MUST NOT reject unknown roles, but MAY warn.- A consuming runtime/deployer SHOULD treat an unsatisfied
requiredrequirement as an error and an unsatisfied optional requirement as a warning. The spec defines the declaration and this SHOULD; enforcement and resolution are implementation-defined.
Examples
Example 1: Basic Usage
A plain conversational pack that needs only a primary LLM:
requires:
providers:
- default
This expands to one required requirement: {key: default, role: llm, required: true}.
Example 2: Advanced Usage
A retrieval-augmented support pack with an optional eval judge:
requires:
providers:
- key: default
role: llm
description: Primary support agent. Needs tool-use and a large context window.
capabilities:
min_context_tokens: 32000
tool_use: true
- key: embeddings
role: embedding
description: Embeds the knowledge base for retrieval.
capabilities:
embedding_dimensions: 1536
- key: reranker
role: inference
required: false
description: Cross-encoder that reorders retrieved passages by relevance.
capabilities:
# open, provider-specific capabilities — namespaced to avoid future clashes
x-task: reranking
x-max-documents: 100
- key: judge
role: llm
required: false
description: Optional LLM judge for the eval suite; a strong reasoning model.
A deployer can now: verify the workspace provides an llm and an embedding provider (coverage), resolve each key to a concrete provider (binding), and warn if the resolved default model is absent from the pack's tested_models (parity).
Drawbacks
- Another optional section for authors to maintain; it can drift from actual usage unless a runtime infers or validates it.
- An open
rolevocabulary means runtimes may diverge on non-core roles. capabilitiesis open and advisory: the spec defines only a few well-known fields, so nuanced or provider-specific matching still leans on the prosedescriptionand namespaced custom keys — and because the object is open, an unrecognised key (including a typo of a well-known field) is silently accepted rather than flagged.
Alternatives
Alternative 1: Infer requirements at runtime (no schema change)
Each runtime infers needs from features it can already see (evals with an LLM judge → needs judge; semantic memory/RAG → needs embedding; always → default llm). Rejected as the normative model: an open, multi-runtime spec cannot mandate inference rules, inferred needs are invisible and non-portable across tools, and authors lose the ability to state intent (and the human description). Inference is retained only as an optional runtime convenience that MAY pre-populate or supplement authored requirements.
Alternative 2: Reuse the variable binding mechanism (a provider kind)
Extend RFC 0003's variable binding with a provider kind. Rejected: variable bindings populate template variables from per-request context (context/session/env/header); provider requirements are pack-level runtime dependencies. The two operate at different layers and lifetimes; conflating them overloads one construct with two meanings.
Alternative 3: Top-level providers block
Rejected: providers reads as concrete provider definitions (endpoints/models), which the portable pack must not contain. requires.providers is clearer about intent and extensible to other requirement categories.
Adoption Strategy
Backward Compatibility
- Fully backward compatible
requires is optional. Packs without it validate and behave exactly as today: runtimes apply their existing defaults (typically a single default llm). Adding the block is purely additive.
Migration Path
None required. Authors opt in when they want explicit requirements, coverage checks, or auto-binding. Tooling MAY offer to generate a requires.providers block from a runtime's inferred needs as a convenience.
Unresolved Questions
The questions raised in the initial draft are resolved in this revision (see Detailed Design):
roleopenness — an open string, with PromptKit's roles (llm,embedding,tts,stt,image,inference) as suggested values; runtimes may extend, validators must not reject unknown roles.- Structured capabilities — included now as an optional
ProviderCapabilitiesobject, reusing RFC 0004's media-type vocabulary for modalities. defaultreservation — a reserved key meaning "primary LLM", but optional.- Same-role discrimination —
keyis the sole discriminator;description+capabilitiescarry the distinction.
None outstanding.
Implementation Plan (Optional)
-
Phase 1: Schema
- Add
requires(root) andProviderRequirement($defs) topromptpack.schema.json. - Validation test fixtures (valid + invalid).
- Add
-
Phase 2: Documentation
- New "Provider Requirements" section in
promptpack-docs, with thedefaultconvention and thetested_modelsrelationship.
- New "Provider Requirements" section in
-
Phase 3: Runtime adoption (non-normative)
- Reference implementations read
requires.providers. - Deployers validate coverage, resolve/auto-bind by
role, and warn ontested_modelsparity.
- Reference implementations read
Testing Strategy (Optional)
Validation Tests
- A pack with no
requiresvalidates (backward compatibility). - String shorthand
"default"expands to an llm requirement. - Object form with
key+rolevalidates; missingkeyorroleis invalid. - Unknown property under
requiresor a requirement object is invalid (additionalProperties: false). - Duplicate
keyvalues are invalid.
Compatibility Tests
- All existing example packs (RFCs 0001–0011) validate unchanged.
Documentation Impact (Optional)
- New spec section: "Provider Requirements".
- Examples in
promptpack-docs. - Document the
defaultprimary-LLM convention and therequiresvstested_modelsdistinction.
Future Considerations (Optional)
Richer capability matching
ProviderCapabilities covers the common cases (modalities, context window, tool-use, structured output, embedding dimension). A follow-up RFC could extend it — a shared role/modality registry, version or pricing constraints, or region/residency requirements — once concrete matching needs emerge.
Other requirement categories
requires is intentionally a container. Future RFCs could add requires.tools or requires.skills for other host-supplied dependencies, reusing the same optional-but-strict pattern.
Revision History
- 2026-06-25: Initial draft.
- 2026-06-25: Resolved open questions — open
roleset (PromptKit values incl.inference); added optional structuredProviderCapabilitiesreusing RFC 0004 media types;defaultreserved-but-optional;keyas sole discriminator. - 2026-06-25: Made
ProviderCapabilitiesan open object (additionalProperties: true) so open-ended providers (e.g.role: inference) can express arbitrary, namespaced capabilities; well-known fields still validated. - 2026-06-25: Implemented in spec v1.5.1 — added the optional top-level
requiresblock withrequires.providers, and theProviderRequirementandProviderCapabilities$defs, toschema/promptpack.schema.json. Patch bump: the field is optional and advisory (no runtime behavior change). Status → Implemented.
References
- RFC 0003 (Template Variables) — the variable
bindingmodel, a distinct concern. - RFC 0006 (Evals) — the judge-model dependency this formalises.
- RFC 0008 (Skills) — precedent for an optional top-level extension block.
tested_modelsmetadata — provenance, complementary to requirements.