diff --git a/python/packages/claude/tests/test_claude_agent.py b/python/packages/claude/tests/test_claude_agent.py index 87816a139c..d08744caef 100644 --- a/python/packages/claude/tests/test_claude_agent.py +++ b/python/packages/claude/tests/test_claude_agent.py @@ -1181,6 +1181,7 @@ class TestClaudeAgentTelemetry: mock_client = self._create_mock_client(messages) monkeypatch.setattr(OBSERVABILITY_SETTINGS, "enable_instrumentation", False) + monkeypatch.setattr(OBSERVABILITY_SETTINGS, "enable_sensitive_data", False) with ( patch("agent_framework_claude._agent.ClaudeSDKClient", return_value=mock_client), diff --git a/python/packages/core/agent_framework/observability.py b/python/packages/core/agent_framework/observability.py index 371f6db297..051319926f 100644 --- a/python/packages/core/agent_framework/observability.py +++ b/python/packages/core/agent_framework/observability.py @@ -691,7 +691,7 @@ class ObservabilitySettings: Model diagnostics are enabled if either diagnostic is enabled or diagnostic with sensitive events is enabled. """ - return self.enable_instrumentation or self.enable_sensitive_data + return self.enable_instrumentation @property def SENSITIVE_DATA_ENABLED(self) -> bool: diff --git a/python/packages/core/tests/core/test_observability.py b/python/packages/core/tests/core/test_observability.py index 3bb8787de6..71b59a351b 100644 --- a/python/packages/core/tests/core/test_observability.py +++ b/python/packages/core/tests/core/test_observability.py @@ -1015,30 +1015,6 @@ def test_observability_settings_is_setup_initial(monkeypatch): assert settings.is_setup is False -@pytest.mark.parametrize( - ("enable_instrumentation", "enable_sensitive_data", "expected"), - [ - (False, False, False), - (True, False, True), - (False, True, True), - (True, True, True), - ], -) -def test_observability_settings_enabled_property( - monkeypatch, enable_instrumentation: bool, enable_sensitive_data: bool, expected: bool -): - """ENABLED is True when either instrumentation or sensitive data is enabled.""" - from agent_framework.observability import ObservabilitySettings - - monkeypatch.delenv("ENABLE_INSTRUMENTATION", raising=False) - monkeypatch.delenv("ENABLE_SENSITIVE_DATA", raising=False) - settings = ObservabilitySettings( - enable_instrumentation=enable_instrumentation, - enable_sensitive_data=enable_sensitive_data, - ) - assert settings.ENABLED is expected - - # region Test enable_instrumentation function diff --git a/python/samples/02-agents/observability/README.md b/python/samples/02-agents/observability/README.md index 49a1c8a9d4..95aa3fc174 100644 --- a/python/samples/02-agents/observability/README.md +++ b/python/samples/02-agents/observability/README.md @@ -173,7 +173,7 @@ Finally we have `VS_CODE_EXTENSION_PORT` which you can set to a port, which can The framework will emit observability data when the `ENABLE_INSTRUMENTATION` environment variable is set to `true`. If both are `true` then it will also emit sensitive information. When these are not set, or set to false, you can use the `enable_instrumentation()` function from the `agent_framework.observability` module to turn on instrumentation programmatically. This is useful when you want to control this via code instead of environment variables. -> **Note**: Sensitive information includes prompts, responses, and more, and should only be enabled in a development or test environment. It is not recommended to enable this in production environments as it may expose sensitive data. Enabling this will automatically enable instrumentation if it is not already enabled. +> **Note**: Sensitive information includes prompts, responses, and more, and should only be enabled in a development or test environment. It is not recommended to enable this in production environments as it may expose sensitive data. The two other variables, `ENABLE_CONSOLE_EXPORTERS` and `VS_CODE_EXTENSION_PORT`, are used to configure where the observability data is sent. Those are only activated when calling `configure_otel_providers()`. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/.dockerignore index 008e6e6616..0d6619bae0 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/.dockerignore @@ -3,4 +3,5 @@ __pycache__ *.pyc *.pyo *.pyd -.Python \ No newline at end of file +.Python +.env \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/.dockerignore index 008e6e6616..0d6619bae0 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/.dockerignore @@ -3,4 +3,5 @@ __pycache__ *.pyc *.pyo *.pyd -.Python \ No newline at end of file +.Python +.env \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/.dockerignore index 008e6e6616..0d6619bae0 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/.dockerignore @@ -3,4 +3,5 @@ __pycache__ *.pyc *.pyo *.pyd -.Python \ No newline at end of file +.Python +.env \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/.dockerignore index 008e6e6616..0d6619bae0 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/.dockerignore @@ -3,4 +3,5 @@ __pycache__ *.pyc *.pyo *.pyd -.Python \ No newline at end of file +.Python +.env \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/.dockerignore index 008e6e6616..0d6619bae0 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/.dockerignore @@ -3,4 +3,5 @@ __pycache__ *.pyc *.pyo *.pyd -.Python \ No newline at end of file +.Python +.env \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore index 1ec65f9d1c..41c70d3269 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore @@ -4,6 +4,7 @@ __pycache__ *.pyo *.pyd .Python +.env # Local-only client tooling and sample data; not needed inside the agent image. resources/ diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.dockerignore index 008e6e6616..0d6619bae0 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.dockerignore +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.dockerignore @@ -3,4 +3,5 @@ __pycache__ *.pyc *.pyo *.pyd -.Python \ No newline at end of file +.Python +.env \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.env.example b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.env.example index 27c4305120..f53b64c8c5 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.env.example +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/.env.example @@ -1,5 +1,4 @@ FOUNDRY_PROJECT_ENDPOINT="..." AZURE_AI_MODEL_DEPLOYMENT_NAME="..." ENABLE_INSTRUMENTATION=true -ENABLE_SENSITIVE_DATA=true -APPLICATIONINSIGHTS_CONNECTION_STRING="..." \ No newline at end of file +ENABLE_SENSITIVE_DATA=true \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/README.md index 84b8bd1bf6..9f08baa168 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/README.md @@ -16,11 +16,9 @@ The agent is hosted using the [Agent Framework](https://github.com/microsoft/age ### Instrumentation -Agent Framework is [**natively instrumented**](https://learn.microsoft.com/en-us/agent-framework/agents/observability?pivots=programming-language-python) to capture diagnostics and telemetry for agent execution, but it's turned off by default. This sample demonstrates how to enable instrumentation via environment variables in `agent.manifest.yaml` and `anget.yaml`. The relevant environment variables are `ENABLE_INSTRUMENTATION` and `ENABLE_SENSITIVE_DATA`, which can be set to `true` to enable diagnostics and capture sensitive events respectively. +Agent Framework is [**natively instrumented**](https://learn.microsoft.com/en-us/agent-framework/agents/observability?pivots=programming-language-python) to capture diagnostics and telemetry for agent execution, but it's turned off by default. This sample demonstrates how to enable instrumentation via environment variables in `agent.manifest.yaml` and `agent.yaml`. The relevant environment variables are `ENABLE_INSTRUMENTATION` and `ENABLE_SENSITIVE_DATA`, which can be set to `true` to enable diagnostics and capture sensitive events respectively. -Foundry Hosted Agent has built-in observability thus you don't need to set up exporters manually to capture telemetry from your code. The traces, metrics, and logs generated by the agent are automatically collected and made available through Foundry's observability stack via Azure Monitor/Application Insights. The `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable is injected when the agent is deployed to Foundry, however it is still required to be set in your local `.env` file or your environment if you want to run the agent host locally and have telemetry sent to Application Insights from your local environment. - -> Setting `ENABLE_SENSITIVE_DATA` to `true` will automatically enable `ENABLE_INSTRUMENTATION` as well, since capturing sensitive events requires that instrumentation be enabled. +Foundry Hosted Agent has built-in observability thus you don't need to set up exporters manually to capture telemetry from your code. The traces, metrics, and logs generated by the agent are automatically collected and made available through Foundry's observability stack via Azure Monitor/Application Insights. The `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable is injected when the agent is deployed to Foundry, however it is still required to be set in your environment if you want to run the agent host locally and have telemetry sent to Application Insights from your local environment. ## Running the Agent Host @@ -28,12 +26,10 @@ Follow the instructions in the [Running the Agent Host Locally](../../README.md# ## Interacting with the agent -> Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. - -Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: +> Because the observability exporters are managed by Foundry, this sample must be run using `azd ai agent run`. Run this sample using `python main.py` will not send telemetry to Application Insights. ```bash -curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "What is the current weather?"}' +azd ai agent run --local "What is the current weather?" ``` A couple of spans will be created for this request from Agent Framework's instrumentation, representing the generation of the response by the agent: diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/main.py index faf7767bfc..5672df11f7 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/main.py +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/07_observability/main.py @@ -16,9 +16,9 @@ from pydantic import Field load_dotenv() -@tool(approval_mode="never_require") +@tool(approval_mode="never_require", description="Get the current location of the user.") def get_current_location() -> str: - """Get the current working directory.""" + """Get the current location of the agent.""" locations = ["New York", "London", "Paris", "Tokyo"] return locations[randint(0, len(locations) - 1)]