arize-phoenix 2.11.1__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arize-phoenix might be problematic. Click here for more details.
- {arize_phoenix-2.11.1.dist-info → arize_phoenix-3.0.1.dist-info}/METADATA +25 -22
- {arize_phoenix-2.11.1.dist-info → arize_phoenix-3.0.1.dist-info}/RECORD +15 -16
- phoenix/config.py +5 -3
- phoenix/trace/exporter.py +24 -9
- phoenix/trace/langchain/__init__.py +25 -3
- phoenix/trace/langchain/instrumentor.py +23 -29
- phoenix/trace/langchain/tracer.py +32 -418
- phoenix/trace/llama_index/callback.py +21 -658
- phoenix/trace/openai/instrumentor.py +19 -676
- phoenix/trace/otel.py +8 -1
- phoenix/trace/tracer.py +84 -107
- phoenix/version.py +1 -1
- phoenix/trace/llama_index/streaming.py +0 -92
- {arize_phoenix-2.11.1.dist-info → arize_phoenix-3.0.1.dist-info}/WHEEL +0 -0
- {arize_phoenix-2.11.1.dist-info → arize_phoenix-3.0.1.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-2.11.1.dist-info → arize_phoenix-3.0.1.dist-info}/licenses/LICENSE +0 -0
phoenix/trace/otel.py
CHANGED
|
@@ -19,6 +19,7 @@ from typing import (
|
|
|
19
19
|
cast,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
+
import numpy as np
|
|
22
23
|
import opentelemetry.proto.trace.v1.trace_pb2 as otlp
|
|
23
24
|
from opentelemetry.proto.common.v1.common_pb2 import AnyValue, ArrayValue, KeyValue
|
|
24
25
|
from opentelemetry.util.types import Attributes, AttributeValue
|
|
@@ -320,7 +321,11 @@ def encode(span: Span) -> otlp.Span:
|
|
|
320
321
|
attributes[key] = json.dumps(value)
|
|
321
322
|
else:
|
|
322
323
|
attributes.update(_flatten_mapping(value, key))
|
|
323
|
-
elif
|
|
324
|
+
elif (
|
|
325
|
+
not isinstance(value, str)
|
|
326
|
+
and (isinstance(value, Sequence) or isinstance(value, np.ndarray))
|
|
327
|
+
and _has_mapping(value)
|
|
328
|
+
):
|
|
324
329
|
attributes.pop(key, None)
|
|
325
330
|
attributes.update(_flatten_sequence(value, key))
|
|
326
331
|
|
|
@@ -413,6 +418,8 @@ def _encode_attributes(attributes: Attributes) -> Iterator[KeyValue]:
|
|
|
413
418
|
if not attributes:
|
|
414
419
|
return
|
|
415
420
|
for key, value in attributes.items():
|
|
421
|
+
if isinstance(value, np.ndarray):
|
|
422
|
+
value = value.tolist()
|
|
416
423
|
yield KeyValue(key=key, value=_encode_value(value))
|
|
417
424
|
|
|
418
425
|
|
phoenix/trace/tracer.py
CHANGED
|
@@ -1,122 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module is defunct and will be removed in the future. It's currently
|
|
3
|
+
maintaining a dummy class to avoid breaking any import code.
|
|
4
|
+
"""
|
|
1
5
|
import logging
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from .schemas import (
|
|
8
|
-
Span,
|
|
9
|
-
SpanAttributes,
|
|
10
|
-
SpanContext,
|
|
11
|
-
SpanConversationAttributes,
|
|
12
|
-
SpanEvent,
|
|
13
|
-
SpanID,
|
|
14
|
-
SpanKind,
|
|
15
|
-
SpanStatusCode,
|
|
16
|
-
TraceID,
|
|
17
|
-
)
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any, Iterator, Protocol
|
|
8
|
+
|
|
9
|
+
from phoenix.trace.exporter import HttpExporter
|
|
18
10
|
|
|
19
11
|
logger = logging.getLogger(__name__)
|
|
20
|
-
logger.addHandler(logging.NullHandler())
|
|
21
12
|
|
|
22
13
|
|
|
23
14
|
class SpanExporter(Protocol):
|
|
24
|
-
def export(self,
|
|
15
|
+
def export(self, _: Any) -> None:
|
|
25
16
|
...
|
|
26
17
|
|
|
27
18
|
|
|
19
|
+
_DEFUNCT_MSG = (
|
|
20
|
+
"`phoenix.trace.tracer.Tracer` is defunct in the current version of Phoenix, "
|
|
21
|
+
"and will be removed in the future. For a migration guide, see "
|
|
22
|
+
"https://github.com/Arize-ai/phoenix/blob/main/MIGRATION.md"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_USE_ENV_MSG = """
|
|
26
|
+
Setting the Phoenix endpoint via HttpExporter() is no longer supported.
|
|
27
|
+
Please use environment variables instead:
|
|
28
|
+
- os.environ["PHOENIX_HOST"] = "127.0.0.1"
|
|
29
|
+
- os.environ["PHOENIX_PORT"] = "54321"
|
|
30
|
+
- os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "http://127.0.0.1:54321
|
|
31
|
+
For a migration guide, see https://github.com/Arize-ai/phoenix/blob/main/MIGRATION.md
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
_ON_APPEND_DEPRECATION_MSG = (
|
|
35
|
+
"OpenInference has been updated for full OpenTelemetry compliance. The ability to set "
|
|
36
|
+
"`on_append` callbacks are removed. For a migration guide, see "
|
|
37
|
+
"https://github.com/Arize-ai/phoenix/blob/main/MIGRATION.md"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _show_deprecation_warnings(obj: object, *args: Any, **kwargs: Any) -> None:
|
|
42
|
+
if args or kwargs:
|
|
43
|
+
logger.warning(
|
|
44
|
+
f"{obj.__class__.__name__}() no longer takes any arguments. "
|
|
45
|
+
"The arguments provided has been ignored. For a migration guide, "
|
|
46
|
+
"see https://github.com/Arize-ai/phoenix/blob/main/MIGRATION.md"
|
|
47
|
+
)
|
|
48
|
+
if any(callable(arg) for arg in args) or "callback" in kwargs:
|
|
49
|
+
logger.warning(
|
|
50
|
+
"The `callback` argument is defunct and no longer has any effect. "
|
|
51
|
+
"If you need access to spans for processing, some options include "
|
|
52
|
+
"exporting spans from Phoenix or adding a SpanProcessor to the "
|
|
53
|
+
"OpenTelemetry TracerProvider. For a migration guide, "
|
|
54
|
+
"see https://github.com/Arize-ai/phoenix/blob/main/MIGRATION.md"
|
|
55
|
+
)
|
|
56
|
+
if any(isinstance(arg, HttpExporter) for arg in args):
|
|
57
|
+
logger.warning(_USE_ENV_MSG)
|
|
58
|
+
|
|
59
|
+
|
|
28
60
|
class Tracer:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"""
|
|
48
|
-
Create a new Tracer. A Tracer's main purpose is to create spans.
|
|
49
|
-
Serialization should be handled by a separate component.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
on_append:
|
|
53
|
-
A callback function that will be called when a span is
|
|
54
|
-
created and appended to the buffer. This is useful for
|
|
55
|
-
serializing data to a file or sending it to a remote server.
|
|
56
|
-
"""
|
|
57
|
-
self.span_buffer = []
|
|
58
|
-
self.on_append = on_append
|
|
59
|
-
self._exporter: Optional[SpanExporter] = exporter
|
|
60
|
-
self._lock = RLock()
|
|
61
|
-
super().__init__(*args, **kwargs)
|
|
62
|
-
|
|
63
|
-
def create_span(
|
|
64
|
-
self,
|
|
65
|
-
name: str,
|
|
66
|
-
span_kind: SpanKind,
|
|
67
|
-
start_time: datetime,
|
|
68
|
-
end_time: Optional[datetime] = None,
|
|
69
|
-
status_code: SpanStatusCode = SpanStatusCode.UNSET,
|
|
70
|
-
status_message: Optional[str] = "",
|
|
71
|
-
parent_id: Optional[SpanID] = None,
|
|
72
|
-
trace_id: Optional[TraceID] = None,
|
|
73
|
-
attributes: Optional[SpanAttributes] = None,
|
|
74
|
-
events: Optional[List[SpanEvent]] = None,
|
|
75
|
-
conversation: Optional[SpanConversationAttributes] = None,
|
|
76
|
-
span_id: Optional[SpanID] = None,
|
|
77
|
-
) -> Span:
|
|
78
|
-
"""
|
|
79
|
-
create_span creates a new span with the given name and options.
|
|
80
|
-
"""
|
|
81
|
-
# If no trace_id is provided, generate a new one
|
|
82
|
-
if trace_id is None:
|
|
83
|
-
trace_id = TraceID(uuid4())
|
|
84
|
-
|
|
85
|
-
# If no attributes are provided, create an empty dict
|
|
86
|
-
if attributes is None:
|
|
87
|
-
attributes = {}
|
|
88
|
-
|
|
89
|
-
# If no events are provided, create an empty list
|
|
90
|
-
if events is None:
|
|
91
|
-
events = []
|
|
92
|
-
|
|
93
|
-
span = Span(
|
|
94
|
-
name=name,
|
|
95
|
-
context=SpanContext(trace_id=trace_id, span_id=span_id or SpanID(uuid4())),
|
|
96
|
-
span_kind=span_kind,
|
|
97
|
-
parent_id=parent_id,
|
|
98
|
-
start_time=start_time,
|
|
99
|
-
end_time=end_time,
|
|
100
|
-
status_code=status_code,
|
|
101
|
-
status_message=status_message if status_message is not None else "",
|
|
102
|
-
attributes=attributes,
|
|
103
|
-
events=events,
|
|
104
|
-
conversation=conversation,
|
|
61
|
+
_exporter: Any
|
|
62
|
+
|
|
63
|
+
def __init__(self, exporter: Any = None, on_append: Any = None) -> None:
|
|
64
|
+
logger.warning(_DEFUNCT_MSG)
|
|
65
|
+
if exporter is not None:
|
|
66
|
+
logger.warning(_USE_ENV_MSG)
|
|
67
|
+
if on_append is not None:
|
|
68
|
+
logger.warning(_ON_APPEND_DEPRECATION_MSG)
|
|
69
|
+
|
|
70
|
+
def create_span(self, *_: Any, **__: Any) -> Any:
|
|
71
|
+
logger.warning(_DEFUNCT_MSG)
|
|
72
|
+
|
|
73
|
+
def get_spans(self) -> Iterator[Any]:
|
|
74
|
+
logger.warning(_DEFUNCT_MSG)
|
|
75
|
+
logger.warning(
|
|
76
|
+
".get_spans() is a defunct method that does nothing. "
|
|
77
|
+
"It will be removed in the future. For a migration guide, "
|
|
78
|
+
"see https://github.com/Arize-ai/phoenix/blob/main/MIGRATION.md"
|
|
105
79
|
)
|
|
80
|
+
return iter(())
|
|
81
|
+
|
|
106
82
|
|
|
107
|
-
|
|
108
|
-
|
|
83
|
+
class _DefunctModule:
|
|
84
|
+
__all__ = ("Tracer", "SpanExporter")
|
|
109
85
|
|
|
110
|
-
|
|
111
|
-
|
|
86
|
+
def __getattr__(self, name: str) -> Any:
|
|
87
|
+
if name == "Tracer":
|
|
88
|
+
logger.warning(_DEFUNCT_MSG)
|
|
89
|
+
return Tracer
|
|
90
|
+
if name == "SpanExporter":
|
|
91
|
+
logger.warning("`SpanExporter` is defunct and will be removed in the future.")
|
|
92
|
+
return SpanExporter
|
|
93
|
+
if name == "_show_deprecation_warnings":
|
|
94
|
+
return _show_deprecation_warnings
|
|
95
|
+
raise AttributeError(f"module {__name__} has no attribute {name}")
|
|
112
96
|
|
|
113
|
-
if self.on_append is not None:
|
|
114
|
-
self.on_append(self.span_buffer)
|
|
115
|
-
return span
|
|
116
97
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
Returns the spans stored in the tracer. This is useful if you are running
|
|
120
|
-
in a notebook environment and you want to inspect the spans.
|
|
121
|
-
"""
|
|
122
|
-
yield from self.span_buffer
|
|
98
|
+
# See e.g. https://stackoverflow.com/a/7668273
|
|
99
|
+
sys.modules[__name__] = _DefunctModule() # type: ignore
|
phoenix/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "3.0.1"
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, timezone
|
|
2
|
-
from typing import TYPE_CHECKING, Generator, List
|
|
3
|
-
|
|
4
|
-
from llama_index.callbacks.schema import TIMESTAMP_FORMAT
|
|
5
|
-
from llama_index.response.schema import StreamingResponse
|
|
6
|
-
|
|
7
|
-
from phoenix.trace.schemas import SpanID, SpanKind, SpanStatusCode
|
|
8
|
-
from phoenix.trace.semantic_conventions import OUTPUT_VALUE
|
|
9
|
-
from phoenix.trace.tracer import Tracer
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from phoenix.trace.llama_index.callback import CBEventData
|
|
13
|
-
|
|
14
|
-
_LOCAL_TZINFO = datetime.now().astimezone().tzinfo
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TokenGenInstrumentor:
|
|
18
|
-
def __init__(
|
|
19
|
-
self, stream: Generator[str, None, None], tracer: Tracer, event_data: "CBEventData"
|
|
20
|
-
):
|
|
21
|
-
self._stream = stream
|
|
22
|
-
self._token_stream: List[str] = []
|
|
23
|
-
self._finished = False
|
|
24
|
-
self._tracer = tracer
|
|
25
|
-
self._event_data = event_data
|
|
26
|
-
|
|
27
|
-
def __iter__(self) -> "TokenGenInstrumentor":
|
|
28
|
-
return self
|
|
29
|
-
|
|
30
|
-
def __next__(self) -> str:
|
|
31
|
-
if self._finished:
|
|
32
|
-
raise StopIteration
|
|
33
|
-
|
|
34
|
-
try:
|
|
35
|
-
value = next(self._stream)
|
|
36
|
-
self._token_stream.append(value)
|
|
37
|
-
return value
|
|
38
|
-
except StopIteration:
|
|
39
|
-
self._finished = True
|
|
40
|
-
self._handle_end_of_stream()
|
|
41
|
-
raise
|
|
42
|
-
|
|
43
|
-
def _handle_end_of_stream(self) -> None:
|
|
44
|
-
# Handle the end-of-stream logic here
|
|
45
|
-
parent_id = self._event_data.parent_id
|
|
46
|
-
output = "".join(self._token_stream)
|
|
47
|
-
start_event = self._event_data.start_event
|
|
48
|
-
if start_event:
|
|
49
|
-
start_time = _timestamp_to_tz_aware_datetime(start_event.time)
|
|
50
|
-
else:
|
|
51
|
-
start_time = datetime.now(timezone.utc)
|
|
52
|
-
attributes = self._event_data.attributes
|
|
53
|
-
attributes.update({OUTPUT_VALUE: output})
|
|
54
|
-
self._tracer.create_span(
|
|
55
|
-
name=self._event_data.name if self._event_data.name else "",
|
|
56
|
-
span_kind=SpanKind.LLM,
|
|
57
|
-
trace_id=self._event_data.trace_id,
|
|
58
|
-
start_time=start_time,
|
|
59
|
-
end_time=datetime.now(timezone.utc),
|
|
60
|
-
status_code=SpanStatusCode.OK,
|
|
61
|
-
status_message="",
|
|
62
|
-
parent_id=SpanID(parent_id) if parent_id else None,
|
|
63
|
-
attributes=self._event_data.attributes,
|
|
64
|
-
events=[],
|
|
65
|
-
conversation=None,
|
|
66
|
-
span_id=SpanID(self._event_data.span_id),
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def instrument_streaming_response(
|
|
71
|
-
response: StreamingResponse,
|
|
72
|
-
tracer: Tracer,
|
|
73
|
-
event_data: "CBEventData",
|
|
74
|
-
) -> StreamingResponse:
|
|
75
|
-
if response.response_gen is not None:
|
|
76
|
-
response.response_gen = TokenGenInstrumentor(response.response_gen, tracer, event_data) # type: ignore
|
|
77
|
-
return response
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def _timestamp_to_tz_aware_datetime(timestamp: str) -> datetime:
|
|
81
|
-
"""Converts a timestamp string to a timezone-aware datetime."""
|
|
82
|
-
return _tz_naive_to_tz_aware_datetime(_timestamp_to_tz_naive_datetime(timestamp))
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _timestamp_to_tz_naive_datetime(timestamp: str) -> datetime:
|
|
86
|
-
"""Converts a timestamp string to a timezone-naive datetime."""
|
|
87
|
-
return datetime.strptime(timestamp, TIMESTAMP_FORMAT)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _tz_naive_to_tz_aware_datetime(timestamp: datetime) -> datetime:
|
|
91
|
-
"""Converts a timezone-naive datetime to a timezone-aware datetime."""
|
|
92
|
-
return timestamp.replace(tzinfo=_LOCAL_TZINFO)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|