Skip to content

Implement proposal 0046 (chat-prompt rendering)#105

Merged
chris-colinsky merged 2 commits into
mainfrom
feature/0046-chat-prompt-rendering
Jun 1, 2026
Merged

Implement proposal 0046 (chat-prompt rendering)#105
chris-colinsky merged 2 commits into
mainfrom
feature/0046-chat-prompt-rendering

Conversation

@chris-colinsky

Copy link
Copy Markdown
Member

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

  • Type surface (prompts/prompt.py): existing Prompt class renamed to TextPrompt; new ChatPrompt carrying chat_template: list[ChatSegment]; ChatSegment discriminates ContentSegment (role + text-OR-content-blocks) and PlaceholderSegment; ContentBlockTemplate covers TextBlockTemplate, ImageURLBlockTemplate, ImageInlineBlockTemplate. Prompt = TextPrompt | ChatPrompt is the discriminated-union alias.
  • Render path (prompts/manager.py): PromptManager.render accepts a placeholders: Mapping[str, Sequence[Message]] | None kwarg. 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.
  • Langfuse backend (prompts/backends/langfuse.py): ChatPromptClient maps to ChatPrompt; placeholder markers map to PlaceholderSegment. Malformed placeholder names bypass construction-time validation via model_construct 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; malformed substitutions surface as prompt_render_error at the prompt boundary.
  • Conformance fixtures 017-031: activated against the extended prompt-management harness (chat_template + placeholders directives; image-block YAML shape mapping; message-dict structural compare).
  • Manifest + version pin: spec submodule v0.37.0 → v0.38.0; conformance.toml adds the 0046 entry; AGENTS.md regenerated.

Breaking change

Prompt is now a type alias (discriminated union over TextPrompt | ChatPrompt). Existing Prompt(...) instantiation MUST update to TextPrompt(...); type annotations using Prompt as return / parameter continue to work via the alias. The Langfuse backend no longer raises on Langfuse chat prompts — it returns ChatPrompt. Per spec §6 narrowing, Text-prompts render to exactly one UserMessage; multi-message / multimodal prompts use the Chat variant.

Test plan

  • Full suite: uv run pytest tests/ — 1063 passed, 208 skipped, 3 deselected
  • All 15 new conformance fixtures (017-031) pass against the extended harness
  • 5 new construction-time validation unit tests cover placeholder regex / duplicate / role-block / empty-block / valid-name paths
  • Cache-key stability regression test asserts chat-segment cache keys are SHA-256-derived (not salted hash())
  • Langfuse malformed-placeholder fetch-then-render-raise timing test
  • Inline image base64 validation security test

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.
Copilot AI review requested due to automatic review settings June 1, 2026 00:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/ChatPrompt models and chat rendering in PromptManager.
  • 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.

Comment thread tests/conformance/test_prompt_management.py
Comment thread src/openarmature/prompts/backends/langfuse.py Outdated
Comment thread tests/conformance/test_prompt_management.py Outdated
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.
@chris-colinsky chris-colinsky merged commit ccb2d29 into main Jun 1, 2026
6 checks passed
@chris-colinsky chris-colinsky deleted the feature/0046-chat-prompt-rendering branch June 1, 2026 01:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants