Skip to content

Conversation

@pull
Copy link

@pull pull bot commented Aug 29, 2025

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.3)

Can you help keep this open source service alive? 💖 Please sponsor : )

seratch and others added 3 commits August 29, 2025 10:06
This pull request resolves #1564

---------

Co-authored-by: Rohan Mehta <[email protected]>
)

When using LiteLLM with agents framework, the agent run pollutes
metadata with non-string values (like hidden_params dict), causing
subsequent dataclasses.replace() calls to fail with Pydantic validation
errors.

This happens because:
1. Agent.reset_tool_choice defaults to True
2. After tool usage, maybe_reset_tool_choice() calls
dataclasses.replace()
3. LiteLLM adds hidden_params (dict) to ModelSettings.metadata
4. ModelSettings expects metadata: dict[str, str] but gets dict[str,
Any]

```python
#!/usr/bin/env python3
"""
Reproduce script for agents framework bug with LiteLLM metadata pollution.
"""

import asyncio

from agents import Agent, ModelSettings, Runner, function_tool, set_tracing_disabled
from agents.extensions.models.litellm_model import LitellmModel


set_tracing_disabled(disabled=True)

API_KEY = "xxx"
BASE_URL = "xxx"


# Create a simple tool that the agent can call
@function_tool
def get_time() -> str:
    """Get the current time."""
    from datetime import datetime

    return f"Current time: {datetime.now().strftime('%H:%M:%S')}"


async def main():
    print("=== Reproducing LiteLLM metadata pollution bug ===")

    agent = Agent[str](
        name="test_agent",
        model=LitellmModel(model="gpt-4o-mini", base_url=BASE_URL, api_key=API_KEY),
        instructions="You are a helpful assistant. Use the get_time tool when asked about time.",
        tools=[get_time],  # Add the tool to trigger reset_tool_choice behavior
        reset_tool_choice=True,  # This is the default, triggers maybe_reset_tool_choice()
    )

    print(f"Agent reset_tool_choice: {agent.reset_tool_choice}")
    print(f"Agent tools: {[tool.name for tool in agent.tools]}")

    print("\n--- First run: Agent calls tool without metadata ---")
    try:
        result1 = await Runner.run(
            starting_agent=agent,
            input="What time is it?",  # This should trigger the get_time tool
            max_turns=3,
        )
        print(f"✓ First run successful")
        print(f"Result: {result1.final_output}")

    except Exception as e:
        print(f"✗ Run failed: {e}")
        return

    metadata = {
        "version": "1.0.0",
        "user_id": "test_user",
        "session_id": "test_session",
    }
    model_settings = ModelSettings(metadata=metadata)
    agent = Agent[str](
        name="test_agent",
        model=LitellmModel(model="gpt-4o-mini", base_url=BASE_URL, api_key=API_KEY),
        instructions="You are a helpful assistant. Use the get_time tool when asked about time.",
        tools=[get_time],  # Add the tool to trigger reset_tool_choice behavior
        model_settings=model_settings,
        reset_tool_choice=True,  # This is the default, triggers maybe_reset_tool_choice()
    )

    print(f"Initial metadata: {list(model_settings.metadata.keys()) if model_settings.metadata else None}")
    print(f"Agent reset_tool_choice: {agent.reset_tool_choice}")
    print(f"Agent tools: {[tool.name for tool in agent.tools]}")

    print("\n--- Second run: Agent calls tool with metadata ---")
    try:
        result1 = await Runner.run(
            starting_agent=agent,
            input="What time is it?",  # This should trigger the get_time tool
            max_turns=3,
        )
        print(f"✓ Second run successful")
        print(f"Result: {result1.final_output}")

        # Check metadata pollution
        if agent.model_settings.metadata:
            print(f"Metadata keys after second run: {list(agent.model_settings.metadata.keys())}")

            # Show the problematic hidden_params
            if "hidden_params" in agent.model_settings.metadata:
                hidden_params = agent.model_settings.metadata["hidden_params"]
                print(
                    f"✓ hidden_params added: {type(hidden_params)} with {len(hidden_params) if isinstance(hidden_params, dict) else 'N/A'} items"
                )
                print(f"  This violates ModelSettings.metadata: dict[str, str] constraint")

    except Exception as e:
        import traceback

        print(f"✗ Run failed:\n{e}")
        print(f"Traceback:\n{traceback.format_exc()}")
        return


if __name__ == "__main__":
    asyncio.run(main())

```

output
```text
=== Reproducing LiteLLM metadata pollution bug ===
Agent reset_tool_choice: True
Agent tools: ['get_time']

--- First run: Agent calls tool without metadata ---
✓ First run successful
Result: The current time is 14:18:47.
Initial metadata: ['version', 'user_id', 'session_id']
Agent reset_tool_choice: True
Agent tools: ['get_time']

--- Second run: Agent calls tool with metadata ---
✗ Run failed:
1 validation error for ModelSettings
metadata.hidden_params
  Input should be a valid string [type=string_type, input_value={'custom_llm_provider': '...'_response_ms': 797.449}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/string_type
"""

```
@pull pull bot locked and limited conversation to collaborators Aug 29, 2025
@pull pull bot added the ⤵️ pull label Aug 29, 2025
@pull pull bot merged commit de9d1fd into jayeshp19:main Aug 29, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants