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 7caec9018b
commit e5aaa4c4eb
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
system_message = None
# Handle system message for different services
if isinstance(context, LLMContext):
# Universal LLMContext - use adapter to convert and get system message
if hasattr(self, "get_llm_adapter"):
adapter = self.get_llm_adapter()
try:
# Get LLM invocation params which includes system_instruction
params = adapter.get_llm_invocation_params(context)
# settings.system_instruction takes priority (matches service behavior)
if hasattr(self, "_settings") and getattr(
self._settings, "system_instruction", None
):
system_message = self._settings.system_instruction
else:
# Fall back to extracting from context messages
ctx_messages = context.get_messages()
if ctx_messages:
first = ctx_messages[0]
if (
isinstance(params, dict)
and "system_instruction" in params
isinstance(first, dict)
and first.get("role") == "system"
):
system_message = params["system_instruction"]
except Exception as e:
logging.debug(
f"Could not extract system instruction from adapter: {e}"
)
content = first.get("content")
if isinstance(content, str):
system_message = content
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"):
system_message = context.system
elif hasattr(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
# initialize all settings.
params = {}
if hasattr(self, "_settings"):
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)):
params[key] = value
elif value is None: