Skip to content

fix(prompts): accept fetched chat messages in create_prompt and fallback - #2076

Open
v0ropaev wants to merge 1 commit into
langfuse:mainfrom
v0ropaev:fix/prompt-chat-message-type
Open

v0ropaev wants to merge 1 commit into
langfuse:mainfrom
v0ropaev:fix/prompt-chat-message-type

Conversation

@v0ropaev

@v0ropaev v0ropaev commented Oct 29, 2026 •

Copy link
Copy Markdown

What does this PR do?

ChatPromptClient.prompt returns chat messages as {"type": "message", "role": ..., "content": ...}, and create_prompt is typed to accept exactly that shape (ChatMessageWithPlaceholdersDict_Message). The generated ChatMessage model only allows "chatmessage" for type, though, so feeding a fetched chat prompt back into create_prompt fails before anything is sent.

Copying a chat prompt under a new name shows it. I reproduced it with an httpx MockTransport, so no server was involved.

p = langfuse.get_prompt("support-bot", type="chat")
langfuse.create_prompt(name="support-bot-copy", prompt=p.prompt, type="chat")

On main this raises ValidationError: 3 validation errors for CreateChatPromptRequest ... Input should be 'chatmessage' [type=enum, input_value='message', input_type=str]. Building the ChatMessageWithPlaceholdersDict_Message TypedDict by hand fails the same way. Messages without a type key work, which is probably why it went unnoticed.

get_prompt(..., type="chat", fallback=...) has the same problem when the fallback list carries type: "message". It raises ValidationError: 3 validation errors for Prompt_Chat with the same message inside the fetch error handler, so the caller gets an exception instead of the fallback. I know fallback is typed as List[ChatMessageDict], which has no type key. But a fallback that includes a placeholder has to use type keys, and anyone following the SDK's own ChatMessageWithPlaceholdersDict writes "message" for the regular entries. If you would rather keep fallback strict, that part can be dropped and the create_prompt fix stands on its own.

The fix adds a small helper in client.py that copies any dict with type == "message" and sets "chatmessage" instead. Placeholders, messages without a type and pydantic message objects are left as they are, and the caller's list is not mutated. It runs in create_prompt before CreateChatPromptRequest and on the chat fallback before Prompt_Chat. Nothing in langfuse/api/ was touched.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactor
  • Documentation update
  • Tooling, CI, or repo maintenance

Verification

I added two unit tests to tests/unit/test_prompt.py. One creates a prompt from a fetched ChatPromptClient and checks that the request carries chatmessage and placeholder entries. The other passes a type: "message" fallback while the fetch fails and checks that it comes back with is_fallback set and compiles. On main they fail with the CreateChatPromptRequest and Prompt_Chat errors quoted above, and with the change they pass.

I ran everything in the project venv (Python 3.14.4, ruff 0.15.7, mypy 1.19.1) with the LANGFUSE_* values from the CI workflow exported, since the langfuse fixture in test_prompt.py needs them (#1976).

ruff check .                        # All checks passed!
ruff format --check .               # 578 files already formatted
mypy langfuse --no-error-summary    # exit 0, no output
python -m pytest -p no:cacheprovider tests/unit/test_prompt.py -k "fetched_chat_prompt or fallback_with_message_type"
                                    # 2 passed (2 failed without the fix)
python -m pytest -p no:cacheprovider tests/unit
                                    # 1027 passed, 2 skipped

I did not run the e2e suite because it needs a running Langfuse server, and I only ran the tests on Python 3.14.

Checklist

  • I self-reviewed the diff using code_review.md.
  • I added or updated tests for behavior changes.
  • I updated docs, examples, or .env.template if needed.
  • I did not hand-edit generated files; if generated files changed, I used the upstream regeneration path.
  • I did not commit secrets or credentials.

RetriggerView in GreptileConfidence Score: 5/5

This PR appears safe to merge.

Summary

Converts chat dictionaries from type="message" to the API’s type="chatmessage" before creating prompts or building fallbacks.

  • Chat prompts accept regular messages from a fetched prompt.
  • Chat fallback lists with regular messages can return a usable prompt.

Acknowledgment: v0ropaev noted that the existing fallback annotation excludes placeholders; this known limitation was not reported again.

Reviews (1) · Last reviewed commit: "fix(prompts): accept fetched chat messag..." · Reviewed by Greptile

ChatPromptClient exposes chat messages with type="message", and
create_prompt is typed to accept that shape through
ChatMessageWithPlaceholdersDict. The generated API models only allow
"chatmessage" for that field, so passing a fetched prompt's .prompt to
create_prompt raised a ValidationError for CreateChatPromptRequest
before any request was sent. get_prompt failed the same way when such
a list was given as a chat fallback, raising inside the fetch error
path instead of returning the fallback.

Map type="message" to "chatmessage" before building the request and
the fallback Prompt_Chat. Placeholders and messages without a type key
are passed through unchanged, and the caller's list is not modified.

@claude claude Bot 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

This branch has not been deployed

No deployments
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