Implement proposal 0046 (chat-prompt rendering)#105
Merged
Conversation
Spec proposal 0046 (prompt-management §3.1 + §6, v0.38.0) adds the Chat-prompt variant alongside the existing Text-prompt variant. Carries an ordered list of ChatSegment entries — ContentSegment (role + text-template OR content-blocks-template) and PlaceholderSegment (caller-supplied message-list injection at render). Content-block templates mirror llm-provider §3.1 (TextBlockTemplate, ImageURLBlockTemplate, ImageInlineBlockTemplate). Type surface: existing Prompt class renamed to TextPrompt; new ChatPrompt; Prompt = TextPrompt | ChatPrompt as the discriminated- union alias. Breaking change at the instantiation surface (Prompt(...) callers move to TextPrompt(...)); type annotations using Prompt continue to work via the union alias. PromptManager.render gains a placeholders kwarg. Chat prompts render segment-by-segment per spec §6 with strict-undefined per segment and per block; the four §11 error categories (empty content segment, empty block list, unfilled placeholder, role- block compatibility) plus duplicate-placeholder-name and placeholder-regex checks fire at render time (spec-normative trigger). Construction-time validators on ContentSegment, PlaceholderSegment, and ChatPrompt are the optional ergonomic- bonus layer per spec msg-07; harnesses building intentionally- invalid fixtures bypass via model_construct. Langfuse backend: ChatPromptClient maps to ChatPrompt with one ContentSegment per Langfuse chat message; placeholder markers map to PlaceholderSegment. Malformed placeholder names from Langfuse bypass construction-time validation so the §11 error fires at render time per the spec-normative timing contract. Security: inline image base64_data is validated via base64.b64decode(..., validate=True) at render time; a malformed substitution surfaces as prompt_render_error at the prompt boundary rather than as a provider-specific decode error. Conformance fixtures 017-031 activate against an extended prompt-management harness (chat_template + placeholders directives; image-block YAML shape mapping; message-dict structural compare). Spec pin v0.37.0 -> v0.38.0.
There was a problem hiding this comment.
Pull request overview
Implements proposal 0046 by adding chat-prompt rendering alongside the existing text prompt flow, including chat segments, placeholders, content-block templates, Langfuse chat prompt mapping, and conformance harness updates for v0.38.0.
Changes:
- Adds
TextPrompt/ChatPromptmodels and chat rendering inPromptManager. - Extends Langfuse and filesystem prompt backends/tests for the new prompt type surface.
- Updates conformance parsing/harness support plus spec-version metadata/docs.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/openarmature/prompts/prompt.py |
Adds prompt variants, chat/content segment models, and prompt union alias. |
src/openarmature/prompts/manager.py |
Adds chat rendering, placeholders, content-block rendering, and inline image base64 validation. |
src/openarmature/prompts/backends/langfuse.py |
Maps Langfuse chat prompts into ChatPrompt. |
src/openarmature/prompts/backends/filesystem.py |
Returns TextPrompt for filesystem prompts. |
src/openarmature/prompts/__init__.py |
Exports new prompt/chat template types. |
tests/** |
Updates existing prompt tests and adds chat prompt/conformance coverage. |
examples/10-langfuse-observability/main.py |
Updates example prompt construction to TextPrompt. |
src/openarmature/__init__.py, pyproject.toml, conformance.toml, AGENTS.md, CHANGELOG.md |
Updates spec pin/documentation/changelog for v0.38.0/proposal 0046. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CoPilot flagged three issues: a doc-comment mismatch on the fixture image-block mapper and two fail-open patterns where unrecognized shapes silently dropped or coerced instead of surfacing the failure. Image-block fixture mapper: the doc comment claimed media_type was nested inside source; the actual fixture YAML puts it at the outer block level (per llm-provider §3.1.2 the field lives on the block, conditional on source type). Code was correct; fixed the comment. Langfuse backend mapper (_normalized_langfuse_entries): split into _normalized_langfuse_entries (fail-closed) plus _chat_segments_from_normalized. Raises PromptNotFound with a descriptive message naming the unsupported shape when it hits an entry the current mapper doesn't recognize. Matches how the backend handles other fetch-side failures. Added two regression tests using a new _chat_client_with_raw_prompt helper that bypasses the Langfuse SDK's own __init__ filter. Placeholder-injection harness (_message_from_fixture): now handles all four llm-provider §3 roles including tool (the fourth role, intentionally excluded from authored ChatSegment per spec §3.1 but legitimate in caller-supplied placeholder message lists). Raises on unknown / misspelled roles instead of silently coercing to UserMessage.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements proposal 0046 (prompt-management §3.1 + §6 chat-prompt rendering, spec v0.38.0). Adds the Chat-prompt variant alongside the existing Text-prompt variant with role-tagged content segments, placeholder slots for caller-supplied message-list injection, and content-block templates (text + image-URL + image-inline). All 15 new conformance fixtures (017-031) activate against an extended prompt-management harness.
Spec pin v0.37.0 → v0.38.0.
Changes
prompts/prompt.py): existingPromptclass renamed toTextPrompt; newChatPromptcarryingchat_template: list[ChatSegment];ChatSegmentdiscriminatesContentSegment(role + text-OR-content-blocks) andPlaceholderSegment;ContentBlockTemplatecoversTextBlockTemplate,ImageURLBlockTemplate,ImageInlineBlockTemplate.Prompt = TextPrompt | ChatPromptis the discriminated-union alias.prompts/manager.py):PromptManager.renderaccepts aplaceholders: Mapping[str, Sequence[Message]] | Nonekwarg. Chat prompts render segment-by-segment with strict-undefined per segment / per block. Four §11 error categories (empty content segment, empty block list, unfilled placeholder, role-block compatibility) plus duplicate-placeholder-name and placeholder-regex checks fire at render time. Construction-time validators on segments + ChatPrompt are the optional ergonomic-bonus layer.prompts/backends/langfuse.py):ChatPromptClientmaps toChatPrompt; placeholder markers map toPlaceholderSegment. Malformed placeholder names bypass construction-time validation viamodel_constructso the §11 error fires at render-time per the spec-normative timing contract.base64_datais validated viabase64.b64decode(..., validate=True)at render time; malformed substitutions surface asprompt_render_errorat the prompt boundary.conformance.tomladds the 0046 entry; AGENTS.md regenerated.Breaking change
Promptis now a type alias (discriminated union overTextPrompt | ChatPrompt). ExistingPrompt(...)instantiation MUST update toTextPrompt(...); type annotations usingPromptas return / parameter continue to work via the alias. The Langfuse backend no longer raises on Langfuse chat prompts — it returnsChatPrompt. Per spec §6 narrowing, Text-prompts render to exactly oneUserMessage; multi-message / multimodal prompts use the Chat variant.Test plan
uv run pytest tests/— 1063 passed, 208 skipped, 3 deselectedhash())