Skip to content

Python: [Bug]: Error when internal MAF observability packages serialize the OOTB Code Interpreter Tool #6971

Description

Description

With basic observability enabled, I created an agent (FoundryChatClient) that uses the code_interpreter_tool via OpenTelemetry. When running the agent, a JSON serialization error happened saying it couldn't serialize the OOTB tool.

To fix it, I had to create a method to patch the serialization, but it should've worked OOTB:

def patch_code_interpreter_otel_definition() -> None:
    """Avoid SDK telemetry serialization failures for Foundry code interpreter tools."""
    import agent_framework.observability as observability

    if getattr(observability, "_code_interpreter_otel_patch_applied", False):
        return

    original = observability._otel_definition_from_mapping

    def _otel_definition_from_mapping(raw: Mapping[str, Any]) -> dict[str, Any] | None:
        if raw.get("type") == "code_interpreter":
            return {"type": "code_interpreter", "name": "code_interpreter"}
        return original(raw)

    observability._otel_definition_from_mapping = _otel_definition_from_mapping
    setattr(observability, "_code_interpreter_otel_patch_applied", True)

Code Sample

####### CLIENT FILE - JUST INSTANTIATES THE CLIENT

@asynccontextmanager
async def foundry_client() -> AsyncGenerator[FoundryChatClient, None]:
    """Create a Foundry client for one chat session."""
    async with AzureCliCredential() as credential:
        client = FoundryChatClient(
            credential=credential, # connection via Managed Id (RBAC), safer than keys
            project_endpoint=os.getenv("FOUNDRY_PROJECT_ENDPOINT"),
            model=os.getenv("FOUNDRY_MODEL"),
        )
        # configure observability/tracing on Azure AI Foundry, integrated with Azure Monitor
        await client.configure_azure_monitor(
            enable_live_metrics=os.getenv("ENABLE_LIVE_METRICS", "False").lower() == "true",
            enable_sensitive_data=os.getenv("ENABLE_SENSITIVE_DATA", "False").lower() == "true",
        )
        yield client

#### AGENT FILE, LEVERAGING THE CLIENT
class AgentV1(AgentBase):
    """Agent v1 - all tools available and freedom to choose any tool"""

    def __init__(self, chat_client: FoundryChatClient, name: str = "agent_v1") -> None:
        """Initialize the agent."""
        super().__init__(chat_client=chat_client, name=name)

    def get_agent(self) -> Agent:
        """Get the agent instance."""
        tools: list[Any] = [
            get_post_text,
            get_pre_text,
            get_table,
            self.chat_client.get_code_interpreter_tool(), # sandboxed code interpreter defaulting to jupyter container
        ]
        return Agent(
            client=self.chat_client,
            name=self.name,
            instructions=self.instructions,
            description="Agent v1 - all tools available and freedom to choose any tool",
            tools=tools,
            context_providers=[InMemoryHistoryProvider('memory')],
            default_options=FoundryChatOptions( 
                prompt_cache_retention="24h", # prompt caching for 24h, lowers costs
                tool_choice="auto", 
                # parallel tool calls when possible, mapped to Agents SDK parallel_tool_calls
                allow_multiple_tool_calls=True,  
                reasoning={ 
                    'effort': 'medium',
                    'summary': 'auto'
                },
                max_tokens=int(os.getenv("FOUNDRY_MAX_TOKENS", 8000)),
            )
        )

    async def run(
        self,
        message: str,
        session: AgentSession,
        doc: dict[str, Any],
    ) -> str:
        """Run the agent, default implementation."""
        response: AgentResponse[Result] = await self.get_agent().run(
            session=session,
            messages=message,
            function_invocation_kwargs=doc, # doc in run context for tool calls with no LLM processing
            options={"response_format": Result},  # structured outputs for more accurate results
        )
        
        return response.text

On main.py, I have (
    enable_console_exporters=os.getenv("ENABLE_CONSOLE_EXPORTERS", "False").lower() == "true",
) 
And a call to `run()`

When calling `AgentV1.run(...)` the error happened.

Error Messages / Stack Traces

File "/mnt/scratch/repos/ignawacc/ignawacc/.venv/lib/python3.12/site-packages/agent_framework/_agents.py", line 1717, in run
    return super_run(
           ^^^^^^^^^^
  File "/mnt/scratch/repos/ignawacc/ignawacc/.venv/lib/python3.12/site-packages/agent_framework/_middleware.py", line 1371, in run
    return super().run(  # type: ignore[misc, no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/scratch/repos/ignawacc/ignawacc/.venv/lib/python3.12/site-packages/agent_framework/observability.py", line 2061, in run
    return self._trace_agent_invocation(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/scratch/repos/ignawacc/ignawacc/.venv/lib/python3.12/site-packages/agent_framework/observability.py", line 1770, in _trace_agent_invocation
    attributes = _get_span_attributes(
                 ^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/scratch/repos/ignawacc/ignawacc/.venv/lib/python3.12/site-packages/agent_framework/observability.py", line 2495, in _get_span_attributes
    result = transform_func(value) if transform_func else value
             ^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/scratch/repos/ignawacc/ignawacc/.venv/lib/python3.12/site-packages/agent_framework/observability.py", line 2460, in <lambda>
    lambda tools: json.dumps(tools_dict, ensure_ascii=False) if (tools_dict := _tools_to_dict(tools)) else None,
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "/usr/lib/python3.12/json/encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type AutoCodeInterpreterToolParam is not JSON serializable

Package Versions

agent-framework>=1.10.0

Python Version

Python 3.12

Additional Context

No response

Metadata

Metadata

Labels

observabilityUsage: [Issues, PRs], Target: observability related featurespythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions