Skip to content

IntelligentContextManager pair-wise toolUse dropping for Anthropic providers to prevent validation errors #116

@tylersalminen

Description

@tylersalminen

Description

IntelligentContextManager does not (or does not have configurable option to prevent) preserve (or prune pair-wise) tool pairs from conversation history when dropping messages.

To Reproduce

Steps to reproduce the behavior:

  1. use an Anthropic / Bedrock Claude providers model that has strictly enforces conversation message shapes.
  2. stream message using HeadroomStrandsModel that results in an assistant toolUse being dropped while a corresponding user toolResult is retained in the conversation history

Expected Behavior

Some configurable behavior to retain or prune tool use in a pair-wise fashion.

Actual Behavior

Assistant toolUse messages can get dropped into the CCR while corresponding user toolResult messages are retained in message history resulting in hitting validation error from Anthropic's strict API

Code Sample

from os import environ

from strands.models import BedrockModel
from strands.types.content import ContentBlock
from strands.tools import tool
from strands.agent import Agent
from headroom import HeadroomConfig
from headroom.config import IntelligentContextConfig
from headroom.integrations.strands import HeadroomStrandsModel
from json import dumps


environ["HEADROOM_MODEL_LIMITS"] = dumps(
	{
		"anthropic": {
			"context_limits": {
				"us.anthropic.claude-sonnet-4-6": 200000
			}
		}
	}
)

@tool
async def _tool():

	return "test"

async def _test():

	model = HeadroomStrandsModel(
		config=HeadroomConfig(
			intelligent_context=IntelligentContextConfig(
				enabled=True,
				output_buffer_tokens=199000 # force messages to drop for testing
			)
		),
		wrapped_model=BedrockModel(
			model_id="us.anthropic.claude-sonnet-4-6"
		)
	)

	agent = Agent(
		model=model,
		tools=[
			_tool
		]
	)


	# loop until hit tool split issue
	for i in range(0, 100):
			
		await agent.invoke_async(
			[
				ContentBlock(
					text="Call the `_tool` tool"
				)
			]
		)

	# IntelligentContextConfig is never utilized b/c
	# token count never includes content / system blocks

Error Output

botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The number of toolResult blocks at messages.1.content exceeds the number of toolUse blocks of previous turn.

└ Bedrock region: us-east-1

└ Model id: us.anthropic.claude-sonnet-4-6

Environment

Headroom version: 0.5.2
Python version: v3.12.10
OS: Windows 11
LLM Provider: Anthropic

Additional Context

Example of toolResult user message being retained while the corresponding toolUse has been dropped into the CCR:

Image

Example exception:

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions