diff --git a/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp b/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp index 62fa4b38b77..b5cd8a81312 100644 --- a/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp +++ b/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp @@ -91,7 +91,7 @@ _stack_v2_link_span(PyObject* self, PyObject* args, PyObject* kwargs) uint64_t thread_id; uint64_t span_id; uint64_t local_root_span_id; - const char* span_type; + const char* span_type = nullptr; PyThreadState* state = PyThreadState_Get(); @@ -104,10 +104,16 @@ _stack_v2_link_span(PyObject* self, PyObject* args, PyObject* kwargs) static const char* const_kwlist[] = { "span_id", "local_root_span_id", "span_type", NULL }; static char** kwlist = const_cast(const_kwlist); - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KKs", kwlist, &span_id, &local_root_span_id, &span_type)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KKz", kwlist, &span_id, &local_root_span_id, &span_type)) { return NULL; } + // From Python, span_type is a string or None, and when given None, it is passed as a nullptr. + static const std::string empty_string = ""; + if (span_type == nullptr) { + span_type = empty_string.c_str(); + } + ThreadSpanLinks::get_instance().link_span(thread_id, span_id, local_root_span_id, std::string(span_type)); Py_RETURN_NONE; diff --git a/ddtrace/llmobs/_llmobs.py b/ddtrace/llmobs/_llmobs.py index c76f90f7782..f25adb3bff0 100644 --- a/ddtrace/llmobs/_llmobs.py +++ b/ddtrace/llmobs/_llmobs.py @@ -508,7 +508,7 @@ def annotate( if parameters is not None: log.warning("Setting parameters is deprecated, please set parameters and other metadata as tags instead.") cls._tag_params(span, parameters) - if input_data or output_data: + if input_data is not None or output_data is not None: if span_kind == "llm": cls._tag_llm_io(span, input_messages=input_data, output_messages=output_data) elif span_kind == "embedding": @@ -598,9 +598,9 @@ def _tag_text_io(cls, span, input_value=None, output_value=None): """Tags input/output values for non-LLM kind spans. Will be mapped to span's `meta.{input,output}.values` fields. """ - if input_value: + if input_value is not None: span.set_tag_str(INPUT_VALUE, safe_json(input_value)) - if output_value: + if output_value is not None: span.set_tag_str(OUTPUT_VALUE, safe_json(output_value)) @staticmethod diff --git a/releasenotes/notes/fix-numeric-annotations-7cf06b5ceb615282.yaml b/releasenotes/notes/fix-numeric-annotations-7cf06b5ceb615282.yaml new file mode 100644 index 00000000000..05ed3ddb964 --- /dev/null +++ b/releasenotes/notes/fix-numeric-annotations-7cf06b5ceb615282.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + LLM Observability: This fix resolves an issue where input and output values equal to zero were not being annotated + on workflow, task, agent and tool spans when using `LLMObs.annotate`. diff --git a/releasenotes/notes/profiling-stack-v2-endpoint-span-type-none-48a1196296be3742.yaml b/releasenotes/notes/profiling-stack-v2-endpoint-span-type-none-48a1196296be3742.yaml new file mode 100644 index 00000000000..1e4fea09afe --- /dev/null +++ b/releasenotes/notes/profiling-stack-v2-endpoint-span-type-none-48a1196296be3742.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + profiling: resolves an issue where endpoint profiling for stack v2 throws + ``TypeError`` exception when it is given a ``Span`` with ``None`` span_type. diff --git a/tests/llmobs/test_llmobs_service.py b/tests/llmobs/test_llmobs_service.py index 7c51705853d..9f0f4e44710 100644 --- a/tests/llmobs/test_llmobs_service.py +++ b/tests/llmobs/test_llmobs_service.py @@ -471,6 +471,17 @@ def test_annotate_input_string(LLMObs): assert retrieval_span.get_tag(INPUT_VALUE) == "test_input" +def test_annotate_numeric_io(LLMObs): + with LLMObs.task() as task_span: + LLMObs.annotate(span=task_span, input_data=0, output_data=0) + assert task_span.get_tag(INPUT_VALUE) == "0" + assert task_span.get_tag(OUTPUT_VALUE) == "0" + with LLMObs.task() as task_span: + LLMObs.annotate(span=task_span, input_data=1.23, output_data=1.23) + assert task_span.get_tag(INPUT_VALUE) == "1.23" + assert task_span.get_tag(OUTPUT_VALUE) == "1.23" + + def test_annotate_input_serializable_value(LLMObs): with LLMObs.task() as task_span: LLMObs.annotate(span=task_span, input_data=["test_input"])