diff --git a/conformance.toml b/conformance.toml index cc40d69..8e212d6 100644 --- a/conformance.toml +++ b/conformance.toml @@ -29,7 +29,7 @@ [manifest] implementation = "openarmature-python" -spec_pin = "v0.24.0" +spec_pin = "v0.26.0" # Status values: # implemented — shipped behavior matches the proposal's contract @@ -150,8 +150,8 @@ status = "textual-only" since = "0.9.0" note = "Drain snapshot semantic and timeout-input validation already implemented as part of the proposal 0010 impl PR (v0.9.0); no additional module-level work needed." -# Spec v0.23.0 + v0.24.0 batch (proposals 0031, 0032). Both proposals -# have impl work landing across the v0.10.0 release cycle; status +# Spec v0.23.0-v0.26.0 batch (proposals 0031, 0032, 0033, 0034). All +# four have impl work landing across the v0.10.0 release cycle; status # stays `not-yet` until the release PR flips them to `implemented` # with `since = "0.10.0"`. The pinned spec submodule advances ahead # of the impl status because newer fixtures need to be visible to @@ -161,3 +161,9 @@ status = "not-yet" [proposals."0032"] status = "not-yet" + +[proposals."0033"] +status = "not-yet" + +[proposals."0034"] +status = "not-yet" diff --git a/docs/concepts/prompts.md b/docs/concepts/prompts.md index 41aea4f..abede54 100644 --- a/docs/concepts/prompts.md +++ b/docs/concepts/prompts.md @@ -100,7 +100,79 @@ a working-but-wrong prompt, often invisibly. If you need lenient behavior, wrap your variables in your own defaulting layer before passing them to `render()`. -The Python implementation uses Jinja2's `StrictUndefined`. +The Python implementation uses Jinja2's `StrictUndefined`. To opt +out, pass a different `Undefined` subclass at `PromptManager` +construction: + +```python +import jinja2 + +manager = PromptManager(backend, jinja_undefined=jinja2.Undefined) +``` + +`jinja2.Undefined` renders a missing variable as the empty string; +`jinja2.ChainableUndefined` is the other common opt-out for +templates that walk nested attributes. Reach for these only when the +strict default is actively wrong for your workflow. + +## Per-prompt sampling parameters + +A `Prompt` carries an optional `sampling` field — a `SamplingConfig` +sub-record mirroring `RuntimeConfig`'s seven declared fields +(`temperature`, `max_tokens`, `top_p`, `seed`, `frequency_penalty`, +`presence_penalty`, `stop_sequences`) plus the extras pass-through +bag. Backends that source per-prompt config (Langfuse's +`prompt.config`, a filesystem sidecar) populate it; backends that +don't leave it `None`. + +```python +prompt = await manager.fetch("classify", "production") +if prompt.sampling is not None: + response = await provider.complete(messages, config=prompt.sampling) +else: + response = await provider.complete(messages) +``` + +`SamplingConfig` is a subclass of `RuntimeConfig`, so it splats +directly into `provider.complete()` without translation. +`PromptResult.sampling` carries the value verbatim from the source +`Prompt`; rendering doesn't touch it. + +The `FilesystemPromptBackend` reads sidecar config when constructed +with `sampling_source="per-prompt-sidecar"` (reading +`/