Skip to content

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

Description

@ignawacc

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

Assignees

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

Fields

No fields configured for Bug.

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions