Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions newrelic/api/error_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import functools

from newrelic.api.time_trace import current_trace, notice_error
from newrelic.common.async_wrapper import async_wrapper as get_async_wrapper
from newrelic.common.object_wrapper import FunctionWrapper, wrap_object


Expand Down Expand Up @@ -43,17 +44,31 @@ def __exit__(self, exc, value, tb):
)


def ErrorTraceWrapper(wrapped, ignore=None, expected=None, status_code=None):
def wrapper(wrapped, instance, args, kwargs):
parent = current_trace()
def ErrorTraceWrapper(wrapped, ignore=None, expected=None, status_code=None, async_wrapper=None):
def literal_wrapper(wrapped, instance, args, kwargs):
# Determine if the wrapped function is async or sync
wrapper = async_wrapper if async_wrapper is not None else get_async_wrapper(wrapped)
# Sync function path
if not wrapper:
parent = current_trace()
if not parent:
# No active tracing context so just call the wrapped function directly
return wrapped(*args, **kwargs)
# Async function path
else:
# For async functions, the async wrapper will handle trace context propagation
parent = None

if parent is None:
return wrapped(*args, **kwargs)
trace = ErrorTrace(ignore, expected, status_code, parent=parent)

if wrapper:
# The async wrapper handles the context management for us
return wrapper(wrapped, trace)(*args, **kwargs)

with ErrorTrace(ignore, expected, status_code, parent=parent):
with trace:
return wrapped(*args, **kwargs)

return FunctionWrapper(wrapped, wrapper)
return FunctionWrapper(wrapped, literal_wrapper)


def error_trace(ignore=None, expected=None, status_code=None):
Expand Down
24 changes: 24 additions & 0 deletions newrelic/common/llm_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2010 New Relic, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


def _get_llm_metadata(transaction):
# Grab LLM-related custom attributes off of the transaction to store as metadata on LLM events
custom_attrs_dict = transaction._custom_params
llm_metadata_dict = {key: value for key, value in custom_attrs_dict.items() if key.startswith("llm.")}
llm_context_attrs = getattr(transaction, "_llm_context_attrs", None)
if llm_context_attrs:
llm_metadata_dict.update(llm_context_attrs)

return llm_metadata_dict
21 changes: 21 additions & 0 deletions newrelic/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2948,6 +2948,27 @@ def _process_module_builtin_defaults():
"newrelic.hooks.mlmodel_autogen",
"instrument_autogen_agentchat_agents__assistant_agent",
)
_process_module_definition(
"strands.agent.agent", "newrelic.hooks.mlmodel_strands", "instrument_strands_agent_agent"
)
_process_module_definition(
"strands.multiagent.graph", "newrelic.hooks.mlmodel_strands", "instrument_strands_multiagent_graph"
)
_process_module_definition(
"strands.multiagent.swarm", "newrelic.hooks.mlmodel_strands", "instrument_strands_multiagent_swarm"
)
_process_module_definition(
"strands.tools.executors._executor",
"newrelic.hooks.mlmodel_strands",
"instrument_strands_tools_executors__executor",
)
_process_module_definition(
"strands.tools.registry", "newrelic.hooks.mlmodel_strands", "instrument_strands_tools_registry"
)
_process_module_definition(
"strands.models.bedrock", "newrelic.hooks.mlmodel_strands", "instrument_strands_models_bedrock"
)

_process_module_definition("mcp.client.session", "newrelic.hooks.adapter_mcp", "instrument_mcp_client_session")
_process_module_definition(
"mcp.server.fastmcp.tools.tool_manager",
Expand Down
Loading
Loading