Skip to content

Python: Bug: ChatHistoryTruncationReducer cuts off TOOL_CALLS role message making the following TOOL role messages orphan in AgentThread history #12708

@SaurabhNiket231

Description

@SaurabhNiket231

Describe the bug
If I run the reduce() using ChatHistoryTruncationReducer on the ChatHistoryAgentThread thread's _chat_history messages then it truncates to the target count regardless of Tool message which must be the response of preceeding Tool call message and hence creating exception:

semantic_kernel.exceptions.service_exceptions.ServiceResponseException: ("<class 'semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion.AzureChatCompletion'> service failed to complete the prompt", BadRequestError('Error code: 400 - {'error': {'message': "Invalid parameter: messages with role 'tool' must be a response to a preceeding message with 'tool_calls'.", 'type': 'invalid_request_error', 'param': 'messages.[1].role', 'code': None}}'))

To Reproduce
Steps to reproduce the behavior:

  1. Create the ChatHistoryTruncationReducer object with a target count of 3 for example.
    truncation_reducer = ChatHistoryTruncationReducer(
    target_count=3,
    )
  2. If you have an existing thread somewhere in session then load it:

Use the existing agent_thread from session, or create if missing

    if session.agent_thread is None:
         session.agent_thread = ChatHistoryAgentThread(
            chat_history=truncation_reducer
         )
     else:
         truncation_reducer.messages = session.agent_thread._chat_history.messages
         prev_messages_count = len(session.agent_thread._chat_history.messages)
         session.agent_thread._chat_history = truncation_reducer
  1. Run the agent.invoke and invoke the reduce method:
    async for response in agent.invoke(
    messages=user_input, thread=session.agent_thread
    ):
    llm_response = response
    is_reduced = await session.agent_thread._chat_history.reduce()

  2. Ask the question which invokes a tool call which generates at least 4 messages. It then tries to reduce to 3 messages and if the oldest one was a tool_call role message, it gets removed even if there are following "TOOL" role messages exist in the history.

Expected behavior
I expects it to retain the required messages even if the target count is reached and should make it flexible to retain the required order of messages and not truncate in such a way to make the history unloadable.

Screenshots
I am getting below error:

[on_turn_error] unhandled error: ("<class 'semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion.AzureChatCompletion'> service failed to complete the prompt", BadRequestError('Error code: 400 - {'error': {'message': "Invalid parameter: messages with role 'tool' must be a response to a preceeding message with 'tool_calls'.", 'type': 'invalid_request_error', 'param': 'messages.[1].role', 'code': None}}'))
2025-07-12T09:14:17.261300937Z Traceback (most recent call last):
2025-07-12T09:14:17.261554362Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/connectors/ai/open_ai/services/open_ai_handler.py", line 87, in _send_completion_request
2025-07-12T09:14:17.261571980Z response = await self.client.chat.completions.create(**settings_dict)
2025-07-12T09:14:17.261578543Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.261583817Z File "/usr/local/lib/python3.11/site-packages/openai/resources/chat/completions/completions.py", line 2454, in create
2025-07-12T09:14:17.261589751Z return await self._post(
2025-07-12T09:14:17.261594497Z ^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.261599302Z File "/usr/local/lib/python3.11/site-packages/openai/_base_client.py", line 1784, in post
2025-07-12T09:14:17.261604595Z return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
2025-07-12T09:14:17.261609830Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.261614700Z File "/usr/local/lib/python3.11/site-packages/openai/_base_client.py", line 1584, in request
2025-07-12T09:14:17.261619919Z raise self._make_status_error_from_response(err.response) from None
2025-07-12T09:14:17.261629830Z openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid parameter: messages with role 'tool' must be a response to a preceeding message with 'tool_calls'.", 'type': 'invalid_request_error', 'param': 'messages.[1].role', 'code': None}}
2025-07-12T09:14:17.261644903Z
2025-07-12T09:14:17.261650157Z The above exception was the direct cause of the following exception:
2025-07-12T09:14:17.261655219Z
2025-07-12T09:14:17.261660893Z Traceback (most recent call last):
2025-07-12T09:14:17.262008370Z File "/usr/local/lib/python3.11/site-packages/botbuilder/core/bot_adapter.py", line 174, in run_pipeline
2025-07-12T09:14:17.262028128Z return await self._middleware.receive_activity_with_status(
2025-07-12T09:14:17.262034778Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262040097Z File "/usr/local/lib/python3.11/site-packages/botbuilder/core/middleware_set.py", line 69, in receive_activity_with_status
2025-07-12T09:14:17.262047728Z return await self.receive_activity_internal(context, callback)
2025-07-12T09:14:17.262070403Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262075625Z File "/usr/local/lib/python3.11/site-packages/botbuilder/core/middleware_set.py", line 79, in receive_activity_internal
2025-07-12T09:14:17.262081244Z return await callback(context)
2025-07-12T09:14:17.262086017Z ^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262090856Z File "/usr/local/lib/python3.11/site-packages/botbuilder/core/activity_handler.py", line 70, in on_turn
2025-07-12T09:14:17.262096009Z await self.on_message_activity(turn_context)
2025-07-12T09:14:17.262100899Z File "/app/bot_ui/NO_AGENT_chef_buddy.py", line 171, in on_message_activity
2025-07-12T09:14:17.262106349Z async for response in agent.invoke(
2025-07-12T09:14:17.262111181Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/utils/telemetry/agent_diagnostics/decorators.py", line 39, in wrapper_decorator
2025-07-12T09:14:17.262116603Z async for response in invoke_func(*args, **kwargs):
2025-07-12T09:14:17.262121927Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/agents/chat_completion/chat_completion_agent.py", line 354, in invoke
2025-07-12T09:14:17.262127248Z async for response in self._inner_invoke(
2025-07-12T09:14:17.262132084Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/agents/chat_completion/chat_completion_agent.py", line 527, in _inner_invoke
2025-07-12T09:14:17.262137287Z responses = await chat_completion_service.get_chat_message_contents(
2025-07-12T09:14:17.262142570Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262314835Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/connectors/ai/chat_completion_client_base.py", line 139, in get_chat_message_contents
2025-07-12T09:14:17.262335646Z completions = await self._inner_get_chat_message_contents(chat_history, settings)
2025-07-12T09:14:17.262342021Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262346546Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/utils/telemetry/model_diagnostics/decorators.py", line 112, in wrapper_decorator
2025-07-12T09:14:17.262351046Z return await completion_func(*args, **kwargs)
2025-07-12T09:14:17.262355262Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262359351Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/connectors/ai/open_ai/services/open_ai_chat_completion_base.py", line 88, in _inner_get_chat_message_contents
2025-07-12T09:14:17.262364440Z response = await self._send_request(settings)
2025-07-12T09:14:17.262368740Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262373397Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/connectors/ai/open_ai/services/open_ai_handler.py", line 59, in _send_request
2025-07-12T09:14:17.262378081Z return await self._send_completion_request(settings)
2025-07-12T09:14:17.262395877Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-07-12T09:14:17.262400805Z File "/usr/local/lib/python3.11/site-packages/semantic_kernel/connectors/ai/open_ai/services/open_ai_handler.py", line 99, in _send_completion_request
2025-07-12T09:14:17.262406169Z raise ServiceResponseException(
2025-07-12T09:14:17.262411573Z semantic_kernel.exceptions.service_exceptions.ServiceResponseException: ("<class 'semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion.AzureChatCompletion'> service failed to complete the prompt", BadRequestError('Error code: 400 - {'error': {'message': "Invalid parameter: messages with role 'tool' must be a response to a preceeding message with 'tool_calls'.", 'type': 'invalid_request_error', 'param': 'messages.[1].role', 'code': None}}'))

Platform

  • Language: Python
  • Source: [e.g. NuGet package version 0.1.0, pip package version 0.1.0, main branch of repository]
  • AI model: [OpenAI:GPT-4o-mini(2024-07-18)]
  • IDE: VS Code
  • OS: Windows

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpythonPull requests for the Python Semantic Kernel

Type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions