Skip to content

[Chat] Fix MessageNormalizer round-trip for AssistantMessage with tool calls#1996

Open
Amoifr wants to merge 1 commit intosymfony:mainfrom
Amoifr:fix_ai_1972
Open

[Chat] Fix MessageNormalizer round-trip for AssistantMessage with tool calls#1996
Amoifr wants to merge 1 commit intosymfony:mainfrom
Amoifr:fix_ai_1972

Conversation

@Amoifr
Copy link
Copy Markdown
Contributor

@Amoifr Amoifr commented Apr 24, 2026

Q A
Bug fix? yes
New feature? no
Docs? no
Issues Fix #1972
License MIT

Problem

When a conversation containing an AssistantMessage (or ToolCallMessage) with tool calls is persisted through MessageNormalizer and later restored, denormalize() crashes on Undefined array key "function".

Root cause

  • MessageNormalizer::denormalize() reads the OpenAI-style wrapped shape: {id, function: {name, arguments: JSON string}}.
  • MessageNormalizer::normalize() delegates the tool call payload to the outer Serializer. When ToolCallNormalizer is in the chain, the wrapped shape is produced; otherwise the default ObjectNormalizer falls back to the flat shape {id, name, arguments: array}.
  • ToolCallNormalizer was never registered as a service in ai-bundle. In real Symfony apps, the outer serializer therefore produced the flat shape, and denormalize crashed. The chat component's existing round-trip tests only passed because they wire ToolCallNormalizer manually.

Fix (two complementary changes)

  1. ai-bundle — register Symfony\AI\Platform\Contract\Normalizer\Result\ToolCallNormalizer with the serializer.normalizer tag, so the outer serializer produces the wrapped shape the denormalizer expects.
  2. chat — make MessageNormalizer::denormalize() tolerate both shapes (wrapped and flat). This lets apps restore conversations that were persisted before this fix landed (the reporter's own DB rows are in the flat form).

Test plan

  • src/chat — 41 tests pass, including 2 new regression tests exercising the flat-shape path without ToolCallNormalizer in the chain (testItCanDenormalizeAssistantMessageWithFlatToolCalls, testItCanDenormalizeToolCallMessageWithFlatToolCall).
  • src/ai-bundle — 239 tests pass (+1 from this PR verifying the new service registration and tag), identical error count to main (8 pre-existing errors tied to the optional symfony/ai-ovh-platform package, unrelated to this change).
  • phpstan clean on src/chat.

Notes

  • Thanks to @sjahns for the precise diagnosis in the issue — the wrapped-vs-flat asymmetry was described clearly enough that the fix direction was obvious.
  • The toolCallFromPayload() helper centralizes the tolerance so there's a single place to revisit if we ever want to drop the flat-shape fallback later.

…e with tool calls

When a conversation containing an AssistantMessage (or ToolCallMessage)
with tool calls was persisted via MessageNormalizer and later restored,
denormalize() crashed on `Undefined array key "function"`.

Root cause: denormalize() reads the OpenAI-style wrapped shape
`{id, function: {name, arguments}}`, but normalize() produced the
flat ObjectNormalizer fallback shape `{id, name, arguments}` because
ToolCallNormalizer was never registered as a service in ai-bundle.
The chat component's round-trip tests only passed because they wire
ToolCallNormalizer manually.

Two complementary changes:
- ai-bundle: register ToolCallNormalizer as a `serializer.normalizer`
  service so normalize() produces the wrapped shape the denormalizer
  expects.
- chat: make MessageNormalizer::denormalize() tolerate both shapes,
  restoring conversations that were persisted with the flat form
  before this fix landed.

Fix symfony#1972
@carsonbot carsonbot added Bug Something isn't working Chat Issues & PRs about the AI Chat component Status: Needs Review labels Apr 24, 2026
@carsonbot carsonbot changed the title [Chat][AiBundle] Fix MessageNormalizer round-trip for AssistantMessage with tool calls [Chat][AI Bundle] Fix MessageNormalizer round-trip for AssistantMessage with tool calls Apr 24, 2026
@carsonbot carsonbot changed the title [Chat][AI Bundle] Fix MessageNormalizer round-trip for AssistantMessage with tool calls [Chat] Fix MessageNormalizer round-trip for AssistantMessage with tool calls Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working Chat Issues & PRs about the AI Chat component Status: Needs Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Chat] MessageNormalizer cannot round-trip AssistantMessage with tool calls

2 participants