#1631·guardrails

[bug] llm.invocation_parameters is a Python repr and embeds the full prompt

Author: anvmeCreated Aug 17, 2026Updated Aug 22, 2026

Describe the bug trace_llm_call sets the OpenInference llm.invocation_parameters span attribute (guardrails/telemetry/open_inference.py:107-120) with two problems:

  1. The value is a Python repr, not JSON — e.g. {'temperature': 0.3, 'messages': [...], 'model': 'gpt-4o-mini'}. Single quotes mean consumers cannot json.loads it (verified: JSONDecodeError).
  2. It embeds the full messages payload, so the entire prompt is duplicated into what should be model invocation parameters.

Per OpenInference, consumers read llm.invocation_parameters.<name> per key; no such per-key attributes are emitted. Because neither the encoding nor the shape matches, OTel backends (Langfuse, Arize, OpenLIT) show empty model parameters, and prompt content lands in a field users don't expect it in — which matters when masking or redaction is configured for inputs but not for this attribute.

To Reproduce

  1. Register any OTel exporter (e.g. InMemorySpanExporter).
  2. Run:
    python
    guard(model="gpt-4o-mini",
          messages=[{"role": "user", "content": "hi"}],
          temperature=0.3)
  3. Read llm.invocation_parameters off the call span:
    "{'temperature': 0.3, 'messages': [{'role': 'user', 'content': 'hi'}], 'model': 'gpt-4o-mini'}"
    json.loads -> JSONDecodeError
    llm.invocation_parameters.* per-key attributes -> none

Expected behavior Emit llm.invocation_parameters.<name> per parameter, valid JSON where a value is structured, and exclude message/prompt payloads (already carried by input.value and llm.input_messages.*).

Libraries used w/ versions: guardrails-ai==0.11.0, opentelemetry-sdk==1.44.0

Environment details: Python 3.13.9 in a venv; also reproduced in Docker python:3.13-slim.

Additional context Found while adding the Langfuse integration (#1630): Langfuse maps modelParameters from llm.invocation_parameters.* and it stays empty. That integration deliberately does not work around this — parsing a repr would need ast.literal_eval, and mapping the blob would copy the prompt into modelParameters.

Source: guardrails-ai/guardrails