fix: read system_instruction from _settings instead of removed attribute

Replace adapter-based extraction in traced_llm with direct reads from
_settings.system_instruction (priority) and context messages (fallback).
The old approach had three bugs: signature mismatch with Anthropic
adapter, key name inconsistency, and unnecessary overhead from full
message/tools conversion.

Also deduplicate the system instruction in spans -- it was appearing as
both "system" and "param.system_instruction".
This commit is contained in:
Mark Backman
2026-03-20 13:12:40 -04:00
parent 112d1bd375
commit 991fbb82da
2 changed files with 27 additions and 17 deletions

View File

@@ -1 +1 @@
- Fixed telemetry record correct system_instruction in span for LLM services - Fixed stale `system_instruction` in LLM tracing spans by reading from `_settings.system_instruction` instead of the removed `_system_instruction` attribute.

View File

@@ -502,35 +502,45 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) -
# Handle system message for different services # Handle system message for different services
system_message = None system_message = None
# Handle system message for different services
if isinstance(context, LLMContext): if isinstance(context, LLMContext):
# Universal LLMContext - use adapter to convert and get system message # settings.system_instruction takes priority (matches service behavior)
if hasattr(self, "get_llm_adapter"): if hasattr(self, "_settings") and getattr(
adapter = self.get_llm_adapter() self._settings, "system_instruction", None
try: ):
# Get LLM invocation params which includes system_instruction system_message = self._settings.system_instruction
params = adapter.get_llm_invocation_params(context) else:
# Fall back to extracting from context messages
ctx_messages = context.get_messages()
if ctx_messages:
first = ctx_messages[0]
if ( if (
isinstance(params, dict) isinstance(first, dict)
and "system_instruction" in params and first.get("role") == "system"
): ):
system_message = params["system_instruction"] content = first.get("content")
except Exception as e: if isinstance(content, str):
logging.debug( system_message = content
f"Could not extract system instruction from adapter: {e}" elif isinstance(content, list):
) system_message = " ".join(
part.get("text", "")
for part in content
if isinstance(part, dict)
and part.get("type") == "text"
)
elif hasattr(context, "system"): elif hasattr(context, "system"):
system_message = context.system system_message = context.system
elif hasattr(context, "system_message"): elif hasattr(context, "system_message"):
system_message = context.system_message system_message = context.system_message
elif hasattr(self, "_system_instruction"):
system_message = self._system_instruction
# Use given_fields() defensively in case a service doesn't # Use given_fields() defensively in case a service doesn't
# initialize all settings. # initialize all settings.
params = {} params = {}
if hasattr(self, "_settings"): if hasattr(self, "_settings"):
for key, value in self._settings.given_fields().items(): for key, value in self._settings.given_fields().items():
# system_instruction is already captured as the
# "system" span attribute above.
if key == "system_instruction":
continue
if isinstance(value, (int, float, bool, str)): if isinstance(value, (int, float, bool, str)):
params[key] = value params[key] = value
elif value is None: elif value is None: