Skip to content

Commit e3b9106

Browse files
Add Strands tools and agents instrumentation. (#1563)
* Add baseline instrumentation. * Add tool and agent instrumentation. * Add tests file. * Cleanup instrumentation. * Cleanup. Co-authored-by: Tim Pansino <tpansino@newrelic.com> * [MegaLinter] Apply linters fixes * Strands Mock Model (#1551) * Add strands to tox.ini * Add mock models for strands testing * Add simple test file to validate strands mocking * Add baseline instrumentation. * Add tool and agent instrumentation. * Add tests file. * Cleanup instrumentation. * Cleanup. Co-authored-by: Tim Pansino <tpansino@newrelic.com> * Handle additional args in mock model. * Add test to force exception and exercise _handle_tool_streaming_completion_error. * Strands Mock Model (#1551) * Add strands to tox.ini * Add mock models for strands testing * Add simple test file to validate strands mocking * Add baseline instrumentation. * Add tool and agent instrumentation. * Add tests file. * Cleanup instrumentation. * Cleanup. Co-authored-by: Tim Pansino <tpansino@newrelic.com> * Handle additional args in mock model. * Strands Mock Model (#1551) * Add strands to tox.ini * Add mock models for strands testing * Add simple test file to validate strands mocking * Add baseline instrumentation. * Add tool and agent instrumentation. * Cleanup. Co-authored-by: Tim Pansino <tpansino@newrelic.com> * [MegaLinter] Apply linters fixes * Add test to force exception and exercise _handle_tool_streaming_completion_error. * Implement strands context passing instrumentation. * Address review feedback. * [MegaLinter] Apply linters fixes * Remove test_simple.py file. --------- Co-authored-by: Tim Pansino <tpansino@newrelic.com> Co-authored-by: Timothy Pansino <11214426+TimPansino@users.noreply.github.com> Co-authored-by: Tim Pansino <timpansino@gmail.com>
1 parent 7c3f5ac commit e3b9106

File tree

12 files changed

+1004
-52
lines changed

12 files changed

+1004
-52
lines changed

newrelic/api/error_trace.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import functools
1616

1717
from newrelic.api.time_trace import current_trace, notice_error
18+
from newrelic.common.async_wrapper import async_wrapper as get_async_wrapper
1819
from newrelic.common.object_wrapper import FunctionWrapper, wrap_object
1920

2021

@@ -43,17 +44,31 @@ def __exit__(self, exc, value, tb):
4344
)
4445

4546

46-
def ErrorTraceWrapper(wrapped, ignore=None, expected=None, status_code=None):
47-
def wrapper(wrapped, instance, args, kwargs):
48-
parent = current_trace()
47+
def ErrorTraceWrapper(wrapped, ignore=None, expected=None, status_code=None, async_wrapper=None):
48+
def literal_wrapper(wrapped, instance, args, kwargs):
49+
# Determine if the wrapped function is async or sync
50+
wrapper = async_wrapper if async_wrapper is not None else get_async_wrapper(wrapped)
51+
# Sync function path
52+
if not wrapper:
53+
parent = current_trace()
54+
if not parent:
55+
# No active tracing context so just call the wrapped function directly
56+
return wrapped(*args, **kwargs)
57+
# Async function path
58+
else:
59+
# For async functions, the async wrapper will handle trace context propagation
60+
parent = None
4961

50-
if parent is None:
51-
return wrapped(*args, **kwargs)
62+
trace = ErrorTrace(ignore, expected, status_code, parent=parent)
63+
64+
if wrapper:
65+
# The async wrapper handles the context management for us
66+
return wrapper(wrapped, trace)(*args, **kwargs)
5267

53-
with ErrorTrace(ignore, expected, status_code, parent=parent):
68+
with trace:
5469
return wrapped(*args, **kwargs)
5570

56-
return FunctionWrapper(wrapped, wrapper)
71+
return FunctionWrapper(wrapped, literal_wrapper)
5772

5873

5974
def error_trace(ignore=None, expected=None, status_code=None):

newrelic/common/llm_utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
def _get_llm_metadata(transaction):
17+
# Grab LLM-related custom attributes off of the transaction to store as metadata on LLM events
18+
custom_attrs_dict = transaction._custom_params
19+
llm_metadata_dict = {key: value for key, value in custom_attrs_dict.items() if key.startswith("llm.")}
20+
llm_context_attrs = getattr(transaction, "_llm_context_attrs", None)
21+
if llm_context_attrs:
22+
llm_metadata_dict.update(llm_context_attrs)
23+
24+
return llm_metadata_dict

newrelic/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2946,6 +2946,13 @@ def _process_module_builtin_defaults():
29462946
"newrelic.hooks.mlmodel_autogen",
29472947
"instrument_autogen_agentchat_agents__assistant_agent",
29482948
)
2949+
_process_module_definition("strands.agent.agent", "newrelic.hooks.mlmodel_strands", "instrument_agent_agent")
2950+
_process_module_definition(
2951+
"strands.tools.executors._executor", "newrelic.hooks.mlmodel_strands", "instrument_tools_executors__executor"
2952+
)
2953+
_process_module_definition("strands.tools.registry", "newrelic.hooks.mlmodel_strands", "instrument_tools_registry")
2954+
_process_module_definition("strands.models.bedrock", "newrelic.hooks.mlmodel_strands", "instrument_models_bedrock")
2955+
29492956
_process_module_definition("mcp.client.session", "newrelic.hooks.adapter_mcp", "instrument_mcp_client_session")
29502957
_process_module_definition(
29512958
"mcp.server.fastmcp.tools.tool_manager",

0 commit comments

Comments
 (0)