Skip to content

Keep cache_control and other extra keys on OpenAI content parts in ChatMessage #13139

Closed as not planned

Description

@julian-risch

Since #12446, ChatMessage.from_openai_dict_format turns list content into Haystack content parts and drops extra keys on those parts, such as cache_control. Before, the raw list was sent back unchanged, so prompt caching through OpenRouter or LiteLLM worked by accident. Now it silently stops. OpenAI ignores extra keys on content parts, so sending them back is safe there.

Options

  1. Reuse meta["cache_control"] (preferred). Move cache_control from a message's parts into the meta key the Anthropic integration already reads, and have to_openai_dict_format put it back on the message's last text part. This restores caching through OpenRouter and LiteLLM and lets AnthropicChatGenerator cache these messages. A breakpoint placed mid-message moves to the end of the message.
  2. Per-part extras. Add an extra dict to TextContent, like FileContent has, and send each part's unknown keys back. Exact for any key, but it changes a public dataclass, stops joining text parts in system and assistant messages, and passes unchecked keys to every OpenAI-compatible server again.
  3. Keep the raw list in meta and resend it. Lossless, but it doubles image payloads, goes stale when the message is edited, and skips the validation from fix!: parse list-of-parts content in ChatMessage.from_openai_dict_format #12446.

Open question: do stricter OpenAI-compatible servers, such as vLLM, accept cache_control?


👋 Hello there! This issue will be handled internally and isn't open for external contributions. If you'd like to contribute, please take a look at issues labeled contributions welcome or good first issue. We'd really appreciate it!

Activity

  1. added
    P1High priority, add to the next sprint
    on Oct 6, 2026
  2. added this to the 3.4.0 milestone on Oct 6, 2026
  3. anakin87 commented on Oct 12, 2026

    @anakin87
    Member

    I investigated the topic a bit

    • cache_control is not an OpenAI field (accepted but ignored)
    • ChatMessage.from_openai_dict_format is a convenience method for users to convert OpenAI dictionaries into Haystack ChatMessage, never used in Haystack/Core Integrations
    • I find it hard to understand how this can cause issues with Anthropich/OpenRouter/LiteLLM

    For these reasons, I'd be inclined to leave the behavior unchanged (apart from refactoring these conversion functions that got intricate)

    @julian-risch @sjrl feel free to add more details. There may be uses of ChatMessage.from_openai_dict_format that I'm overlooking.

  4. anakin87 commented on Oct 12, 2026

    @anakin87
    Member

    Studying this a bit more, I also realized that part of the complexity of to_openai_dict_format/from_openai_dict_format comes from handling "refusal".
    But I also found that:

    • OpenAIChatGenerator does not use thse helpers and does not preserve this field
    • I could not find any other component in Haystack/Core Integrations that populates ChatMessage.meta["refusal]"

    So, unless there is a use case I am missing, I think that removing this "refusal" handling could simplify the conversion and making this code easier to understand and maintain in the long run.

  5. sjrl commented on Oct 13, 2026

    @sjrl
    Contributor

    @anakin87 I guess a question for me would be should we add the refusal handling to the OpenAIChatGenerator? Its possible this is a feature we have been missing out on or ignoring that we should support as part of the OpenAI spec. WDYT?

  6. anakin87 commented on Oct 13, 2026

    @anakin87
    Member

    @sjrl Yes, I think you're right

  7. julian-risch commented on Oct 14, 2026

    @julian-risch
    MemberAuthor

    Leaving the behavior unchanged except for what you talked about refusal handling sounds good to me too!
    For context, Hayhooks relies on from_openai_dict_format in a couple of places and I believe what worked in the past is that if a user set cache_control on content parts and had that in the content list, Hayhooks passed this through with from_openai_dict_format and to_openai_dict_format and an OpenRouterChatGenerator or LiteLLMChatGenerator could actually receive that and act on it for example by enabling caching for anthropic models.

  8. anakin87 commented on Oct 14, 2026

    @anakin87
    Member

    I'll do the following:

    • do not support cache_control and other extra keys (conversion methods in Hayhooks are just used in code examples)
    • remove refusal handling from conversion methods for now
    • open an issue to track proper refusal support in OpenAIChatGenerator
  9. anakin87 commented on Oct 14, 2026

    @anakin87
    Member

    I opened #13255 to track the support for refusals in OpenAI Chat Generators

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P1High priority, add to the next sprinthandled internallyHandled by the team, not open for external contributions

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions