diff --git a/packages/opentelemetry-instrumentation-together/opentelemetry/instrumentation/together/__init__.py b/packages/opentelemetry-instrumentation-together/opentelemetry/instrumentation/together/__init__.py index 985effb250..9662a2e5e6 100644 --- a/packages/opentelemetry-instrumentation-together/opentelemetry/instrumentation/together/__init__.py +++ b/packages/opentelemetry-instrumentation-together/opentelemetry/instrumentation/together/__init__.py @@ -122,7 +122,14 @@ def _wrap( ) _handle_input(span, event_logger, llm_request_type, kwargs) - response = wrapped(*args, **kwargs) + try: + response = wrapped(*args, **kwargs) + except Exception as exc: + if span.is_recording(): + span.record_exception(exc) + span.set_status(Status(StatusCode.ERROR, str(exc))) + span.end() + raise if response: _handle_response(span, event_logger, llm_request_type, response) diff --git a/packages/opentelemetry-instrumentation-together/tests/test_errors.py b/packages/opentelemetry-instrumentation-together/tests/test_errors.py new file mode 100644 index 0000000000..7731bc043b --- /dev/null +++ b/packages/opentelemetry-instrumentation-together/tests/test_errors.py @@ -0,0 +1,43 @@ +from unittest.mock import MagicMock + +import pytest +from opentelemetry.trace.status import StatusCode + +from opentelemetry.instrumentation.together import _wrap + + +def _make_wrapper(): + tracer = MagicMock() + span = MagicMock() + span.is_recording.return_value = True + tracer.start_span.return_value = span + + wrapper = _wrap(tracer, None, {"method": "chat.completions.ChatCompletions.create", "span_name": "together.chat"}) + return span, wrapper + + +def test_chat_api_error_marks_span_failed(): + span, wrapper = _make_wrapper() + wrapped = MagicMock(side_effect=RuntimeError("401 Unauthorized")) + + with pytest.raises(RuntimeError, match="401 Unauthorized"): + wrapper(wrapped, None, [], {"model": "test-model"}) + + exc_arg = span.record_exception.call_args.args[0] + assert isinstance(exc_arg, RuntimeError) + assert "401 Unauthorized" in str(exc_arg) + span.set_status.assert_called_once() + span.end.assert_called_once() + + +def test_chat_api_error_records_exception_message(): + span, wrapper = _make_wrapper() + wrapped = MagicMock(side_effect=ValueError("bad request")) + + with pytest.raises(ValueError, match="bad request"): + wrapper(wrapped, None, [], {"model": "test-model"}) + + status_arg = span.set_status.call_args.args[0] + assert status_arg.status_code == StatusCode.ERROR + assert status_arg.description == "bad request" + span.end.assert_called_once() diff --git a/packages/opentelemetry-instrumentation-together/uv.lock b/packages/opentelemetry-instrumentation-together/uv.lock index c86b750df5..d15454de31 100644 --- a/packages/opentelemetry-instrumentation-together/uv.lock +++ b/packages/opentelemetry-instrumentation-together/uv.lock @@ -832,7 +832,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-together" -version = "0.53.3" +version = "0.60.0" source = { editable = "." } dependencies = [ { name = "opentelemetry-api" },