0033: prompt-management — Surface Refinements¶
- Status: Accepted
- Author: Chris Colinsky
- Created: 2026-05-26
- Accepted: 2026-05-26
- Targets: spec/prompt-management/spec.md (extends §3 Prompt shape and §4 PromptResult shape with
samplingandobservability_entitiessub-record fields; extends §5 PromptBackend protocol with an informative sidecar convention; extends §6 PromptManager interface with optionalLabelResolverintegration; adds new §7 LabelResolver primitive — renumbers existing §7-§13 by +1; extends §12 (was §11) Cross-spec touchpoints with sampling-to-RuntimeConfig wiring and Langfuse-Prompt-entity-lookup touchpoints); spec/observability/spec.md (clarifies §8.4.4 to namePrompt.observability_entities['langfuse_prompt']as the spec-defined location for the Langfuse Prompt reference, replacing the implementation-definedmetadata-mapping phrasing inherited from proposal 0031) - Related: 0017 (prompt management core), 0024 (LLM span payload + GenAI semconv), 0031 (observability Langfuse mapping), 0032 (llm-provider RuntimeConfig surface refinements)
- Supersedes:
Summary¶
Four additions to the prompt-management capability, all driven by production-deployment patterns surfaced during adoption of proposals 0031 (Langfuse mapping) and 0032 (RuntimeConfig surface):
-
Typed
samplingsub-record onPrompt(§3) andPromptResult(§4). A new optional field carrying per-prompt sampling configuration. The sub-record mirrors the declared-fields-plus-extras shape of llm-provider §6RuntimeConfig(the seven declared fields landed by 0032 —temperature,max_tokens,top_p,seed,frequency_penalty,presence_penalty,stop_sequences— plus an extras mapping for vendor-specific knobs). Callers can splat it directly into aRuntimeConfigconstructor at the LLM call site without per-field translation. The model identifier is NOT part of this sub-record; per-prompt model selection is out of scope (the bound provider determines the model). -
Optional
LabelResolverintegration onPromptManager(§6) plus a new §7 specifying the resolver primitive. ALabelResolvermaps prompt names to labels for deployment-time A/B testing — flip one prompt tostagingorvariant-awithout code changes by updating the resolver's data. The manager MAY be constructed with a resolver;fetch(name)without an explicit label argument consults the resolver. Fallback chain: per-name override → resolver default →"production". Existing §13 (was §12) Out-of-scope-mentions "where the label comes from" is now addressed. -
Informative filesystem sidecar convention on the PromptBackend protocol (§5). A normative addition to the protocol that backends MAY populate
Prompt.samplingfrom any source — Langfuse'sprompt.config, a database row, a sidecar JSON file. Specifically for filesystem backends, the spec recommends (informatively) the conventions<root>/<name>.config.json(per-prompt sidecar) or a unified<root>/prompt_configs.jsonkeyed by name. The file shape itself is informative; the normative contract is on the field appearing on Prompt. -
Typed
observability_entitiesfield onPrompt(§3) andPromptResult(§4). A normativedict[str, Any] | Nonemapping where backends populate backend-keyed references to first-class entities the prompt has been registered as in observability backends. Keys follow a<backend>_<entity>naming convention. This proposal defines one spec-normative key:langfuse_prompt— the Langfuse SDK Prompt-entity reference (the value type is opaque to the spec; per-language SDKs determine the concrete type). Future observability backend mappings (e.g., a Phoenix mapping) define their own keys when they land. This replaces proposal 0031's "implementation-defined underPrompt.metadata" placeholder for the Langfuse Prompt reference — observability §8.4.4 gets a small clarifying touch to read the reference from the new normative location.
Plus two cross-spec touchpoint updates: §12 (was §11) gains a paragraph
on Prompt.sampling → RuntimeConfig wiring at the LLM call site, and
a paragraph on the observability_entities['langfuse_prompt'] lookup
that the §8.4.4 Langfuse Generation linkage rule reads.
No breaking changes. Existing callers see the new fields as optional
(absent on Prompts that don't supply sampling config or observability
entities); existing PromptManagers continue to work without a
LabelResolver. Existing backends are not required to populate
sampling or observability_entities; both fields are opt-in
per-backend. The observability §8.4.4 touch is a clarification of the
already-Accepted v0.23.0 contract (the Langfuse mapping's trigger rule
unchanged — "the prompt's source exposes a Langfuse Prompt reference"
— with the lookup location now spec-defined rather than
implementation-defined); no observability behavior changes for
existing implementations.
Motivation¶
The prompt-management capability shipped at v0.15.0 (proposal 0017)
with a minimal Prompt shape — name, version, label, template,
template_hash, fetched_at, metadata. Three frictions have surfaced
across production adoption since:
- Per-prompt sampling parameters end up in a parallel structure.
When a prompt has prompt-specific tuning (e.g.,
temperature=0.0for a structured-extraction prompt,temperature=0.7for a creative-writing prompt), there's nowhere onPromptto put it. Adopters maintain a parallel JSON file mapping prompt names to sampling configs and a service-side loader that reads it, then manually splat the configs intoRuntimeConfigat every LLM call site. Two patterns recur: - A
prompt_configs.jsonsidecar file structured as{name: {temperature, max_tokens, top_p, ...}}. -
A typed
ModelConfig/PromptConfigdataclass per service that parses the JSON and exposes typed accessors. The §3metadatafield nominally accommodates this (it's implementation-defined), but its lack of typing means every adopter writes the same parsing scaffolding. Folding sampling config into a typedPrompt.samplingsub-record (mirroringRuntimeConfig) replaces ~50-100 lines of per-service scaffolding with one normative field. -
The
labelargument has no deployment-time override path.PromptManager.fetch(name, label)requires the caller to pass the label at every call site. Production deployments wanting to A/B test or canary one specific prompt (segment_semanticshould be onstaging; everything else stays onproduction) have to either hard-code per-prompt label arguments (rebuilds the service for every A/B switch) or maintain a per-service "label resolver" shim that maps prompt names to labels via a config file. Every adopter ends up re-implementing the same shim. ALabelResolverprimitive surfaces the pattern in spec and lets the manager consult it transparently. -
Filesystem backends have no convention for prompt-adjacent metadata. Adopters who use the filesystem reference backend typically maintain a side directory tree (
prompts/templates/<name>.j2alongsideprompts/configs/<name>.jsonor similar) — but the spec doesn't recommend any convention, so every adopter invents one. This is purely a discoverability friction (the spec doesn't prescribe filesystem layout, and shouldn't), but a brief informative recommendation in §5 saves the discovery loop.
Why now¶
Proposal 0032 (just landed at v0.24.0) settled the seven declared
RuntimeConfig fields. With that surface stable, the
Prompt.sampling sub-record can mirror it cleanly without
chicken-and-egg ambiguity ("which fields should sampling carry?"
becomes "exactly the seven declared in RuntimeConfig plus extras").
The two follow-on Langfuse-related proposals (0031 Langfuse mapping +
0032 RuntimeConfig refinements) have closed the observability and
LLM-call surfaces; the prompt-management refinements complete the
adoption set for production deployments wiring OA to managed prompt
backends.
The four additions are independent surface refinements — each addresses a distinct adoption gap. They bundle into one proposal because they share a single driver (production adopters of proposal 0031 + 0032), a single spec (prompt-management), and a short PR-review cycle keeps the related changes coherent.
Design¶
The complete text of the §3 / §4 / §5 / §6 / §7 (new) / §12 (was §11) modifications is reproduced below. Section numbering shifts: current §7 (Variable injection) → §8; §8 (Composite backends) → §9; §9 (PromptGroup) → §10; §10 (Errors) → §11; §11 (Cross-spec touchpoints) → §12; §12 (Determinism) → §13; §13 (Out of scope) → §14.
The spec version under which this lands is determined at acceptance
time and recorded in CHANGELOG.md. Anticipated bump: MINOR
(v0.26.0) — new optional fields, new primitive, no breaking changes.
(Originally drafted as the v0.25.0 candidate; proposal 0034 accepted
ahead of 0033 and shipped as v0.25.0, so this proposal now targets
v0.26.0.)
prompt-management §3 — Prompt shape (extended)¶
A Prompt record:
| Field | Description |
|---|---|
name |
String. The prompt's stable identifier within its backend. Matches the name argument the caller passed to fetch. |
version |
String. The prompt's version identifier within its backend. Implementation-defined: a backend MAY use semver, monotonic integers, content hashes, git short-SHAs, date stamps, or any stable identifier. Two distinct version strings MUST denote distinct prompt contents. |
label |
String. The label under which the prompt was fetched (e.g., "production", "latest", "variant-a"). Backends MAY support multiple labels per prompt; the label is part of the fetch query. |
template |
The unrendered template, in the implementation's chosen template representation (a Jinja2 Template instance, a string, an AST, etc.). The spec does not constrain the in-memory representation; it constrains the render contract (§8 (was §7)). |
template_hash |
String. A stable content-derived hash of the unrendered template. Implementations SHOULD use a cryptographic hash (e.g., SHA-256 hex) over the canonical serialization of the template. The hash MUST be deterministic for identical template content. |
fetched_at |
Timestamp of when this Prompt was fetched from its backend. Implementation-defined precision. When the backend serves a cached result, fetched_at MUST reflect the original fetch time, not the cache hit time. |
sampling |
Optional. A SamplingConfig sub-record carrying per-prompt sampling configuration. Field shape mirrors llm-provider §6 RuntimeConfig: the seven declared fields (temperature, max_tokens, top_p, seed, frequency_penalty, presence_penalty, stop_sequences), all optional, plus an extras mapping for vendor-specific fields per RuntimeConfig's extras-pass-through contract. Per-language implementations SHOULD use the SAME type as RuntimeConfig (or a structurally-compatible subtype) so callers can splat prompt.sampling directly into provider.complete(config=...) without per-field translation. The model identifier is NOT part of SamplingConfig; per-prompt model selection is out of scope (the bound provider determines the model). Absent (None / null / undefined, per the language idiom) when the backend doesn't supply sampling config for this prompt. |
observability_entities |
Optional mapping (dict[str, Any] \| None) carrying backend-keyed references to first-class entities the prompt has been registered as in observability backends. Keys follow <backend>_<entity> naming. Spec-normative keys (this proposal): langfuse_prompt — the Langfuse SDK Prompt-entity reference, used by observability §8.4.4 to establish the Langfuse Generation → Prompt link. Future observability backend mappings define their own keys. Values are opaque to the spec; per-language implementations determine the concrete type (e.g., the Langfuse Python SDK's Prompt class instance, the Langfuse TypeScript SDK's equivalent). Absent / None when the backend doesn't expose any such references; absent keys within a populated mapping signal "this backend's reference is not available." |
metadata |
Optional implementation-defined mapping of additional backend-supplied metadata (e.g., Langfuse tags, file path of origin, other backend-attribution metadata). The spec does not constrain shape. Note that the Langfuse Prompt-entity reference has moved out of this field as of this proposal — it now lives on observability_entities['langfuse_prompt'] so that the observability §8.4.4 lookup has a spec-defined location. |
The name + version + label triple identifies a prompt; the
template_hash lets two prompts with the same name be distinguished
by content (e.g., a Langfuse-backed prompt fetched at two different
times with the same latest label may have different content).
sampling is opt-in per backend. A backend that doesn't supply
sampling config (e.g., a minimal in-memory test backend) returns
prompts with sampling = None. Callers consume the field defensively
(checking for absence) or rely on the language's idiom for unset
optional fields. The spec does NOT mandate a default sampling config
in the absence of a supplied one — callers fall back to
RuntimeConfig() defaults at the provider layer.
prompt-management §4 — PromptResult shape (extended)¶
A PromptResult record:
| Field | Description |
|---|---|
name |
String. Propagated from the source Prompt.name. |
version |
String. Propagated from the source Prompt.version. |
label |
String. Propagated from the source Prompt.label. |
template_hash |
String. Propagated from the source Prompt.template_hash. |
rendered_hash |
String. (Unchanged from v0.15.0.) |
messages |
An ordered, non-empty sequence of Message records, per llm-provider §3. Ready to pass to Provider.complete(). |
variables |
The variable mapping that was used to render. (Unchanged from v0.15.0.) |
fetched_at |
Timestamp. (Unchanged from v0.15.0.) |
rendered_at |
Timestamp. (Unchanged from v0.15.0.) |
sampling |
Propagated from the source Prompt.sampling. Same shape as §3's sampling field; absent when the source Prompt had no sampling config. |
observability_entities |
Propagated from the source Prompt.observability_entities. Same shape as §3's field; carries the same backend-keyed reference mapping the source Prompt had. Rendering does NOT modify the contents. |
The propagation rule for sampling matches the rule for
name / version / label / template_hash: the source Prompt's
value is carried forward verbatim. Rendering does NOT modify or
reinterpret the sampling sub-record.
prompt-management §5 — PromptBackend protocol (extended)¶
The PromptBackend protocol is unchanged at the operation level:
fetch(name, label="production") returns a Prompt. New normative
addition:
Backends MAY populate Prompt.sampling from any source the
backend has access to. Common sources:
- A Langfuse-backed
PromptBackendsourcessamplingfrom Langfuse'sprompt.configfield (Langfuse's own per-prompt config storage). - A filesystem
PromptBackendMAY adopt the convention of loading a sidecar file (see Filesystem sidecar conventions below). - A database-backed backend loads from a per-prompt config column.
- A test / mock backend hard-codes
sampling = Nonefor the prompts it returns.
When a backend supplies Prompt.sampling, it MUST construct the
sub-record per §3's shape (the seven declared fields plus extras
mapping; declared-field types match RuntimeConfig). A backend that
sources from a vendor system with a richer config shape MUST project
to the declared SamplingConfig shape, placing vendor-specific
fields under the extras mapping per §3's extras-pass-through analog
of llm-provider §6.
Filesystem sidecar conventions (informative). Filesystem backends
MAY adopt either of two conventions for sourcing sampling:
- Per-prompt sidecar: for a template at
<root>/<name>.j2, also read<root>/<name>.config.json(or equivalent extension) and populatePrompt.samplingfrom its contents. - Unified config file: read a single
<root>/prompt_configs.jsonat backend construction time, keyed by prompt name; populatePrompt.samplingfrom the entry matching the fetched name.
The two conventions have different top-level shapes:
Per-prompt sidecar — top-level is a single SamplingConfig. The
prompt name comes from the sidecar file's path (<name>.config.json
next to <name>.j2); the file's JSON does NOT include a name field.
Example (informative):
{
"temperature": 0.0,
"max_tokens": 256,
"stop_sequences": ["END"],
"extras": {
"repetition_penalty": 1.05
}
}
Unified config file — top-level is a mapping from prompt name to
SamplingConfig. Example (informative):
{
"classify": {
"temperature": 0.0,
"max_tokens": 256,
"stop_sequences": ["END"]
},
"extract_claims": {
"temperature": 0.2,
"max_tokens": 1024,
"extras": {"repetition_penalty": 1.05}
}
}
The convention is informative; the spec does NOT mandate a specific
filesystem layout. Implementations are free to use either
convention, both, or neither (e.g., loading from a separate config
service). The normative contract is the Prompt.sampling field
itself, not the file convention that produces it.
prompt-management §6 — PromptManager interface (extended)¶
A PromptManager is constructed with one or more PromptBackends
and (per this proposal) an optional LabelResolver. The fetch
operation is extended to consult the resolver when no explicit label
is supplied:
fetch(name, label=None)¶
Async. Fetches a Prompt by name and label, consulting backends in
order per §9 (was §8) fallback semantics. Label resolution:
- If
labelis explicitly supplied (non-None), use it verbatim. Manager passes it through to backendfetch(name, label)calls. - If
labelisNone(or absent) AND the manager has aLabelResolverconfigured, consult the resolver per §7 (new):label = resolver.resolve(name). Manager passes the resolved label to backends. - If
labelisNone(or absent) AND noLabelResolveris configured, use the default"production". (Backwards-compatible with the v0.15.0 default.)
The default value for the label parameter is None (or the
language's idiomatic "unset" sentinel) rather than the string
"production". This makes the resolver / default chain explicit:
callers who want to force-pass "production" continue to do so;
callers who want the resolver to decide simply omit the argument.
The render(prompt, variables) operation is unchanged. The
get(name, label, variables) convenience continues to apply the
same label-resolution rule.
prompt-management §7 — LabelResolver (new section)¶
A LabelResolver is an optional helper that maps prompt names to
labels for deployment-time A/B testing — flip one prompt to
staging or variant-a without code changes by updating the
resolver's data.
Operation:
resolve(name) -> str¶
Synchronous. Returns the label to use when fetching the prompt named
name. Pure function; deterministic for given resolver state.
Fallback chain. Implementations MUST resolve in this order:
- Per-name override. If the resolver has a specific label
mapped for
name, return it. (Highest precedence.) - Default override. If the resolver has a default label
configured (e.g., a
"default"key in a mapping source), return that. - Spec fallback. Return
"production". (Lowest precedence, backwards-compatible with v0.15.0's default.)
The contract is on the precedence order and on the spec-fallback value, not on how the resolver stores its data. Implementations MAY back resolvers with:
- A static mapping (in-memory dict / record).
- A JSON file (e.g.,
prompt_labels.jsonkeyed by prompt name). - An environment-variable lookup.
- A remote config service (resolution result MAY be cached).
Configuration shape (informative). A common pattern is a JSON file structured as:
Under this shape, the resolver returns "staging" for
segment_semantic, "variant-a" for extract_claim_candidates,
and "production" (the file's "default" value, which equals the
spec-fallback in this case) for every other prompt name.
No resolver, no problem. A PromptManager constructed without
a LabelResolver follows the §6 rule's step 3 directly: when no
label is supplied at fetch time, use "production". Existing
v0.15.0 callers continue to work without modification.
prompt-management §12 (was §11) — Cross-spec touchpoints (extended)¶
The existing §11 touchpoints (llm-provider §3 message shape; observability §5.5 LLM-provider span attributes) continue to apply. This proposal adds two more:
llm-provider §6 (RuntimeConfig wiring)¶
When a managed prompt has Prompt.sampling set (per §3), the LLM
call site MAY thread the sub-record through to
provider.complete(config=...)'s RuntimeConfig argument. The
declared-fields-plus-extras shape mirrors RuntimeConfig exactly,
so the wiring is a direct splat in the implementation's idiom (e.g.,
Python RuntimeConfig(**prompt.sampling.fields),
TypeScript { ...prompt.sampling }).
The §6 of llm-provider null-skip semantics applies once the values
reach RuntimeConfig: declared fields with value None /
undefined in Prompt.sampling MUST be omitted from the wire body
per llm-provider §6. The PromptManager itself does NOT enforce
null-skip — it merely propagates sampling to the PromptResult; the
wire-layer skip happens at the RuntimeConfig construction site.
Per-language ergonomics may further provide a convenience method
that combines render() + complete() (e.g., a render_and_call()
or invoke_with_prompt() helper that internally splats
PromptResult.sampling into the LLM call). Convenience helpers are
out of spec scope; the contract this section establishes is the
shape-compatibility between Prompt.sampling and RuntimeConfig.
observability §8.4.4 (Langfuse Prompt-entity reference lookup)¶
Proposal 0031's observability §8.4.4 specifies when a Langfuse
Generation observation MUST be linked to a Langfuse Prompt entity:
"when the prompt's source exposes a Langfuse Prompt reference."
The v0.23.0 phrasing leaves the lookup location
implementation-defined under Prompt.metadata. This proposal moves
the reference to a spec-defined location on Prompt:
Prompt.observability_entities['langfuse_prompt'].
When that key is present (value is the opaque Langfuse SDK Prompt
reference for the rendered prompt), the Langfuse observer MUST
establish the native link per §8.4.4 case 1. When the key is absent
or observability_entities itself is None, §8.4.4 case 2 applies
(metadata-only, no Prompt-entity link). The trigger semantic is
unchanged; only the lookup location is now spec-defined.
Observability §8.4.4 of the spec is updated in tandem with this
proposal's acceptance to read the reference from
observability_entities['langfuse_prompt'] rather than from
metadata. Implementations of the Langfuse mapping that previously
read from impl-defined metadata keys update their lookup
accordingly.
Conformance fixtures¶
Four new fixtures land at acceptance:
-
spec/prompt-management/conformance/013-prompt-sampling-from-backend.{yaml,md}— verifies §3Prompt.samplingpropagation. A mock backend returns aPromptwith a populatedsamplingsub-record (covering all seven declared fields plus one extras key). The fixture asserts thatPrompt.samplingcarries the supplied values verbatim and thatPromptResult.sampling(after render) is identical. -
spec/prompt-management/conformance/014-prompt-sampling-absent.{yaml,md}— verifies §3's "opt-in per backend" semantic. A mock backend returns aPromptwithsampling = None. The fixture asserts that the field is absent / null on bothPromptand the resultingPromptResult, and that no defaulting happens at the manager layer. -
spec/prompt-management/conformance/015-label-resolver-fallback-chain.{yaml,md}— verifies §7 LabelResolver fallback chain. One case exercises all three levels: fetch("segment_semantic", label=None)→ resolver returns"staging"(per-name override).fetch("extract_claims", label=None)→ resolver returns"variant-a"(per-name override).fetch("classify", label=None)→ resolver returns its"default"value (default override; in the fixture's resolver config, the default is"production", matching the spec fallback).fetch("unknown_prompt", label=None)with a resolver configured with NO"default"key → spec fallback returns"production".-
fetch("any", label="explicit")→ resolver is NOT consulted; label is"explicit"verbatim. -
spec/prompt-management/conformance/016-prompt-observability-entities-propagation.{yaml,md}— verifies §3Prompt.observability_entitiespropagation through fetch → render → PromptResult. A mock backend returns aPromptwith anobservability_entitiesmapping containing a cannedlangfuse_promptreference value. The fixture asserts thatPrompt.observability_entities['langfuse_prompt']carries the supplied value verbatim, thatPromptResult.observability_entities(after render) carries the same value, and that rendering does NOT modify the mapping. A second case exercises the absent semantic (observability_entities = None) and asserts the field is null on both sides.
The harness conventions extend with three new primitives:
prompt_backend.populates_sampling: {sampling-config block}— when used by a mock backend, attaches a sampling sub-record to every prompt the backend returns.prompt_backend.populates_observability_entities: {mapping}— when used by a mock backend, attaches the suppliedobservability_entitiesmapping to every prompt the backend returns. Values are opaque sentinel strings the harness uses for equality assertions (no real Langfuse SDK objects are constructed at fixture time).label_resolver: {mapping or null}— configures the manager with a LabelResolver backed by the supplied mapping; whennull, no resolver is configured (manager uses the spec-fallback).
Versioning¶
MINOR bump. The spec's whole-spec SemVer increments to v0.26.0 on acceptance:
- Adds optional
samplingfield to prompt-management §3Promptand §4PromptResult. - Adds optional
observability_entitiesfield to prompt-management §3Promptand §4PromptResult. - Adds the informative filesystem sidecar convention to §5.
- Extends §6
PromptManager.fetch()with optionalLabelResolverconsultation; defaultlabelparameter shifts from"production"toNone/sentinel. - Adds new §7
LabelResolverprimitive section; renumbers existing §7-§13 → §8-§14. - Adds §12 (was §11) cross-spec touchpoints with llm-provider §6
RuntimeConfigand observability §8.4.4 Langfuse-Prompt-entity lookup. - Updates observability §8.4.4 to read the Langfuse Prompt reference
from
Prompt.observability_entities['langfuse_prompt']rather than from an implementation-definedmetadatakey. The §8.4.4 trigger semantic is unchanged; only the lookup location is now spec-defined. - Adds four conformance fixtures (013, 014, 015, 016).
- No breaking changes. Callers passing
label="production"explicitly continue to work. Callers usingfetch(name)without an explicit label continue to get"production"when no resolver is configured (spec-fallback path). Implementations of the observability §8.4.4 Langfuse mapping update their lookup from the v0.23.0 implementation-definedmetadatakey to the v0.26.0 spec-definedobservability_entities['langfuse_prompt']; the visible behavior is unchanged.
CHANGELOG entry references this proposal.
Out of scope¶
For this proposal specifically:
- Per-prompt model selection. Adding a model identifier to
Prompt.samplingwould require either an llm-provider §5 change (per-call model override oncomplete()) or PromptManager dispatch logic that selects a Provider based on prompt-supplied model identity. Both are larger surfaces than this proposal warrants. Production adopters that need this today route through per-prompt Provider instances (one Provider per model); a future proposal MAY tackle per-call model override once the usage patterns settle. - Backend-level sampling-config caching / invalidation. A Langfuse backend that fetches sampling config alongside the template might want TTL-based caching. The caching policy is per-backend implementation per prompt-management §5 (the v0.15.0 contract that backends MAY cache their own results); this proposal does not constrain it.
SamplingConfigvalidation. The declared fields mirrorRuntimeConfig; range validation is deferred to the provider (per 0032's resolution of that question). ThePrompt.samplingshape is not subject to additional validation at the prompt-management layer.- Per-language partial-config constructors. Per-language
ergonomic helpers (e.g., a
SamplingConfig.from_partial(**kwargs)constructor that filters language-null kwargs) are implementation ergonomics, paralleling llm-provider §6's same out-of-scope item. - Resolver hot-reload. A
LabelResolverbacked by a file MAY reload on file change; the spec does not mandate it (and a hot reload during an active invocation would violate determinism for that invocation). Implementations decide.
Open questions¶
None. Three questions flagged at draft time are settled in the proposal text above:
samplingfield name — settled atsampling. Accurately bounds the field to its actual contents (sampling parameters mirroringRuntimeConfig); alternativesprompt_config/runtime/paramseither overpromise scope (prompt_configsuggests broader per-prompt configuration the field doesn't carry) or are ambiguous / collision-prone (runtimeambiguous out of context,paramscollides withparametersfor tool JSON Schema in llm-provider §4). Avoids Pydantic's reservedmodel_configattribute. Matches the OA spec's existing convention of precise, scoped field names (template_hash,rendered_hash,fetched_at).LabelResolversection placement — placed as new §7 with §7-§13 renumbered to §8-§14. The resolver is a first-class primitive that PromptManager consumes; sibling placement matches the dependency graph and gives the primitive its own discoverable section.- Langfuse Prompt-entity reference location — normalized to
Prompt.observability_entities['langfuse_prompt'](new typed field). Replaces proposal 0031's implementation-definedmetadata-key placeholder. Theobservability_entitiesmapping is generic enough to accommodate future observability backends (Phoenix, Honeycomb LLM lens) without per-vendor pollution on Prompt's primary surface, while giving §8.4.4 a spec-defined lookup target.