Stamp uploaded references refs onto spans and logs (#3763)
* Stamp gen ai refs on spans and logs * remove log body stamping * fix lint
This commit is contained in:
parent
ee947cec0c
commit
4fb00c9894
|
|
@ -7,5 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Add upload hook to genai utils to implement semconv v1.37.
|
||||
|
||||
The hook uses [`fsspec`](https://filesystem-spec.readthedocs.io/en/latest/) to support
|
||||
various pluggable backends.
|
||||
([#3752](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3752))
|
||||
([#3759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3752))
|
||||
([#3763](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3763))
|
||||
- Add a utility to parse the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable.
|
||||
Add `gen_ai_latest_experimental` as a new value to the Sem Conv stability flag ([#3716](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3716)).
|
||||
|
|
|
|||
|
|
@ -22,16 +22,28 @@ import threading
|
|||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
from dataclasses import asdict, dataclass
|
||||
from functools import partial
|
||||
from typing import Any, Callable, Literal, TextIO, cast
|
||||
from typing import Any, Callable, Final, Literal, TextIO, cast
|
||||
from uuid import uuid4
|
||||
|
||||
import fsspec
|
||||
|
||||
from opentelemetry._logs import LogRecord
|
||||
from opentelemetry.semconv._incubating.attributes import gen_ai_attributes
|
||||
from opentelemetry.trace import Span
|
||||
from opentelemetry.util.genai import types
|
||||
from opentelemetry.util.genai.upload_hook import UploadHook
|
||||
|
||||
GEN_AI_INPUT_MESSAGES_REF: Final = (
|
||||
gen_ai_attributes.GEN_AI_INPUT_MESSAGES + "_ref"
|
||||
)
|
||||
GEN_AI_OUTPUT_MESSAGES_REF: Final = (
|
||||
gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES + "_ref"
|
||||
)
|
||||
GEN_AI_SYSTEM_INSTRUCTIONS_REF: Final = (
|
||||
gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS + "_ref"
|
||||
)
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -177,7 +189,19 @@ class FsspecUploadHook(UploadHook):
|
|||
},
|
||||
)
|
||||
|
||||
# TODO: stamp the refs on telemetry
|
||||
# stamp the refs on telemetry
|
||||
references = {
|
||||
GEN_AI_INPUT_MESSAGES_REF: ref_names.inputs_ref,
|
||||
GEN_AI_OUTPUT_MESSAGES_REF: ref_names.outputs_ref,
|
||||
GEN_AI_SYSTEM_INSTRUCTIONS_REF: ref_names.system_instruction_ref,
|
||||
}
|
||||
if span:
|
||||
span.set_attributes(references)
|
||||
if log_record:
|
||||
log_record.attributes = {
|
||||
**(log_record.attributes or {}),
|
||||
**references,
|
||||
}
|
||||
|
||||
def shutdown(self) -> None:
|
||||
# TODO: support timeout
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ from unittest import TestCase
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import fsspec
|
||||
from fsspec.implementations.memory import MemoryFileSystem
|
||||
|
||||
from opentelemetry._logs import LogRecord
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
from opentelemetry.util.genai import types
|
||||
from opentelemetry.util.genai._fsspec_upload.fsspec_hook import (
|
||||
|
|
@ -200,9 +200,6 @@ class FsspecUploaderTest(TestCase):
|
|||
|
||||
|
||||
class TestFsspecUploadHookIntegration(TestBase):
|
||||
def setUp(self):
|
||||
MemoryFileSystem.store.clear()
|
||||
|
||||
def assert_fsspec_equal(self, path: str, value: str) -> None:
|
||||
with fsspec.open(path, "r") as file:
|
||||
self.assertEqual(file.read(), value)
|
||||
|
|
@ -211,13 +208,67 @@ class TestFsspecUploadHookIntegration(TestBase):
|
|||
hook = FsspecUploadHook(
|
||||
base_path=BASE_PATH,
|
||||
)
|
||||
tracer = self.tracer_provider.get_tracer(__name__)
|
||||
log_record = LogRecord()
|
||||
|
||||
with tracer.start_as_current_span("chat mymodel") as span:
|
||||
hook.upload(
|
||||
inputs=FAKE_INPUTS,
|
||||
outputs=FAKE_OUTPUTS,
|
||||
system_instruction=FAKE_SYSTEM_INSTRUCTION,
|
||||
span=span,
|
||||
log_record=log_record,
|
||||
)
|
||||
hook.shutdown()
|
||||
|
||||
finished_spans = self.get_finished_spans()
|
||||
self.assertEqual(len(finished_spans), 1)
|
||||
span = finished_spans[0]
|
||||
|
||||
# span attributes, log attributes, and log body have refs
|
||||
for attributes in [
|
||||
span.attributes,
|
||||
log_record.attributes,
|
||||
]:
|
||||
for ref_key in [
|
||||
"gen_ai.input.messages_ref",
|
||||
"gen_ai.output.messages_ref",
|
||||
"gen_ai.system_instructions_ref",
|
||||
]:
|
||||
self.assertIn(ref_key, attributes)
|
||||
|
||||
self.assert_fsspec_equal(
|
||||
span.attributes["gen_ai.input.messages_ref"],
|
||||
'[{"role":"user","parts":[{"content":"What is the capital of France?","type":"text"}]}]',
|
||||
)
|
||||
self.assert_fsspec_equal(
|
||||
span.attributes["gen_ai.output.messages_ref"],
|
||||
'[{"role":"assistant","parts":[{"content":"Paris","type":"text"}],"finish_reason":"stop"}]',
|
||||
)
|
||||
self.assert_fsspec_equal(
|
||||
span.attributes["gen_ai.system_instructions_ref"],
|
||||
'[{"content":"You are a helpful assistant.","type":"text"}]',
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def upload_with_log(log_record: LogRecord):
|
||||
hook = FsspecUploadHook(
|
||||
base_path=BASE_PATH,
|
||||
)
|
||||
|
||||
hook.upload(
|
||||
inputs=FAKE_INPUTS,
|
||||
outputs=FAKE_OUTPUTS,
|
||||
system_instruction=FAKE_SYSTEM_INSTRUCTION,
|
||||
log_record=log_record,
|
||||
)
|
||||
hook.shutdown()
|
||||
|
||||
fs = fsspec.open(BASE_PATH).fs
|
||||
self.assertEqual(len(fs.ls(BASE_PATH)), 3)
|
||||
# TODO: test stamped telemetry
|
||||
def test_stamps_empty_log(self):
|
||||
log_record = LogRecord()
|
||||
self.upload_with_log(log_record)
|
||||
|
||||
# stamp on both body and attributes
|
||||
self.assertIn("gen_ai.input.messages_ref", log_record.attributes)
|
||||
self.assertIn("gen_ai.output.messages_ref", log_record.attributes)
|
||||
self.assertIn("gen_ai.system_instructions_ref", log_record.attributes)
|
||||
|
|
|
|||
Loading…
Reference in New Issue