arize-phoenix 3.14.1__py3-none-any.whl → 3.15.0__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-3.14.1.dist-info → arize_phoenix-3.15.0.dist-info}/METADATA +1 -1
- {arize_phoenix-3.14.1.dist-info → arize_phoenix-3.15.0.dist-info}/RECORD +30 -29
- phoenix/config.py +30 -1
- phoenix/core/project.py +8 -0
- phoenix/core/traces.py +20 -4
- phoenix/server/api/routers/evaluation_handler.py +3 -2
- phoenix/server/api/routers/span_handler.py +1 -1
- phoenix/server/api/routers/trace_handler.py +1 -1
- phoenix/server/api/schema.py +27 -5
- phoenix/server/api/types/Span.py +1 -1
- phoenix/server/app.py +1 -1
- phoenix/server/main.py +4 -22
- phoenix/server/static/index.js +452 -432
- phoenix/session/client.py +52 -11
- phoenix/session/session.py +9 -0
- phoenix/storage/span_store/__init__.py +23 -0
- phoenix/trace/__init__.py +8 -1
- phoenix/trace/dsl/helpers.py +10 -4
- phoenix/trace/langchain/instrumentor.py +2 -1
- phoenix/trace/llama_index/callback.py +2 -1
- phoenix/trace/openai/instrumentor.py +2 -1
- phoenix/trace/projects.py +13 -3
- phoenix/trace/span_json_encoder.py +8 -0
- phoenix/utilities/project.py +1 -1
- phoenix/utilities/span_store.py +23 -0
- phoenix/version.py +1 -1
- phoenix/storage/spanstore/__init__.py +0 -9
- {arize_phoenix-3.14.1.dist-info → arize_phoenix-3.15.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-3.14.1.dist-info → arize_phoenix-3.15.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-3.14.1.dist-info → arize_phoenix-3.15.0.dist-info}/licenses/LICENSE +0 -0
- /phoenix/storage/{spanstore → span_store}/text_file.py +0 -0
phoenix/session/client.py
CHANGED
|
@@ -34,15 +34,13 @@ class Client(TraceDataExtractor):
|
|
|
34
34
|
"""
|
|
35
35
|
Client for connecting to a Phoenix server.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
notebook, delegate the request to the active session instead of making HTTP
|
|
45
|
-
requests. This argument is set to False if endpoint is provided explicitly.
|
|
37
|
+
Args:
|
|
38
|
+
endpoint (str, optional): Phoenix server endpoint, e.g. http://localhost:6006. If not
|
|
39
|
+
provided, the endpoint will be inferred from the environment variables.
|
|
40
|
+
use_active_session_if_available (bool, optional): If px.active_session() is available
|
|
41
|
+
in the same runtime, e.g. the same Jupyter notebook, delegate the request to the
|
|
42
|
+
active session instead of making HTTP requests. This argument is set to False if
|
|
43
|
+
endpoint is provided explicitly.
|
|
46
44
|
"""
|
|
47
45
|
self._use_active_session_if_available = use_active_session_if_available and not endpoint
|
|
48
46
|
host = get_env_host()
|
|
@@ -64,6 +62,21 @@ class Client(TraceDataExtractor):
|
|
|
64
62
|
root_spans_only: Optional[bool] = None,
|
|
65
63
|
project_name: Optional[str] = None,
|
|
66
64
|
) -> Optional[Union[pd.DataFrame, List[pd.DataFrame]]]:
|
|
65
|
+
"""
|
|
66
|
+
Queries spans from the Phoenix server or active session based on specified criteria.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
queries (SpanQuery): One or more SpanQuery objects defining the query criteria.
|
|
70
|
+
start_time (datetime, optional): The start time for the query range. Default None.
|
|
71
|
+
stop_time (datetime, optional): The stop time for the query range. Default None.
|
|
72
|
+
root_spans_only (bool, optional): If True, only root spans are returned. Default None.
|
|
73
|
+
project_name (str, optional): The project name to query spans for. This can be set
|
|
74
|
+
using environment variables. If not provided, falls back to the default project.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Union[pd.DataFrame, List[pd.DataFrame]]: A pandas DataFrame or a list of pandas
|
|
78
|
+
DataFrames containing the queried span data, or None if no spans are found.
|
|
79
|
+
"""
|
|
67
80
|
project_name = project_name or get_env_project_name()
|
|
68
81
|
if not queries:
|
|
69
82
|
queries = (SpanQuery(),)
|
|
@@ -108,6 +121,18 @@ class Client(TraceDataExtractor):
|
|
|
108
121
|
self,
|
|
109
122
|
project_name: Optional[str] = None,
|
|
110
123
|
) -> List[Evaluations]:
|
|
124
|
+
"""
|
|
125
|
+
Retrieves evaluations for a given project from the Phoenix server or active session.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
project_name (str, optional): The name of the project to retrieve evaluations for.
|
|
129
|
+
This can be set using environment variables. If not provided, falls back to the
|
|
130
|
+
default project.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
List[Evaluations]: A list of Evaluations objects containing evaluation data. Returns an
|
|
134
|
+
empty list if no evaluations are found.
|
|
135
|
+
"""
|
|
111
136
|
project_name = project_name or get_env_project_name()
|
|
112
137
|
if self._use_active_session_if_available and (session := px.active_session()):
|
|
113
138
|
return session.get_evaluations(project_name=project_name)
|
|
@@ -140,16 +165,32 @@ class Client(TraceDataExtractor):
|
|
|
140
165
|
f"with `import phoenix as px; px.launch_app()`"
|
|
141
166
|
)
|
|
142
167
|
|
|
143
|
-
def log_evaluations(self, *evals: Evaluations) -> None:
|
|
168
|
+
def log_evaluations(self, *evals: Evaluations, project_name: Optional[str] = None) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Logs evaluation data to the Phoenix server.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
evals (Evaluations): One or more Evaluations objects containing the data to log.
|
|
174
|
+
project_name (str, optional): The project name under which to log the evaluations.
|
|
175
|
+
This can be set using environment variables. If not provided, falls back to the
|
|
176
|
+
default project.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
None
|
|
180
|
+
"""
|
|
181
|
+
project_name = project_name or get_env_project_name()
|
|
144
182
|
for evaluation in evals:
|
|
145
183
|
table = evaluation.to_pyarrow_table()
|
|
146
184
|
sink = pa.BufferOutputStream()
|
|
185
|
+
headers = {"content-type": "application/x-pandas-arrow"}
|
|
186
|
+
if project_name:
|
|
187
|
+
headers["project-name"] = project_name
|
|
147
188
|
with pa.ipc.new_stream(sink, table.schema) as writer:
|
|
148
189
|
writer.write_table(table)
|
|
149
190
|
self._session.post(
|
|
150
191
|
urljoin(self._base_url, "/v1/evaluations"),
|
|
151
192
|
data=cast(bytes, sink.getvalue().to_pybytes()),
|
|
152
|
-
headers=
|
|
193
|
+
headers=headers,
|
|
153
194
|
).raise_for_status()
|
|
154
195
|
|
|
155
196
|
|
phoenix/session/session.py
CHANGED
|
@@ -9,6 +9,7 @@ from enum import Enum
|
|
|
9
9
|
from importlib.util import find_spec
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from tempfile import TemporaryDirectory
|
|
12
|
+
from threading import Thread
|
|
12
13
|
from typing import (
|
|
13
14
|
TYPE_CHECKING,
|
|
14
15
|
Any,
|
|
@@ -47,6 +48,7 @@ from phoenix.trace import Evaluations
|
|
|
47
48
|
from phoenix.trace.dsl.query import SpanQuery
|
|
48
49
|
from phoenix.trace.trace_dataset import TraceDataset
|
|
49
50
|
from phoenix.utilities import query_spans
|
|
51
|
+
from phoenix.utilities.span_store import get_span_store, load_traces_data_from_store
|
|
50
52
|
|
|
51
53
|
try:
|
|
52
54
|
from IPython.display import IFrame # type: ignore
|
|
@@ -302,6 +304,12 @@ class ThreadSession(Session):
|
|
|
302
304
|
port=port,
|
|
303
305
|
notebook_env=notebook_env,
|
|
304
306
|
)
|
|
307
|
+
if span_store := get_span_store():
|
|
308
|
+
Thread(
|
|
309
|
+
target=load_traces_data_from_store,
|
|
310
|
+
args=(self.traces, span_store),
|
|
311
|
+
daemon=True,
|
|
312
|
+
).start()
|
|
305
313
|
# Initialize an app service that keeps the server running
|
|
306
314
|
self.app = create_app(
|
|
307
315
|
export_path=self.export_path,
|
|
@@ -309,6 +317,7 @@ class ThreadSession(Session):
|
|
|
309
317
|
corpus=self.corpus,
|
|
310
318
|
traces=self.traces,
|
|
311
319
|
umap_params=self.umap_parameters,
|
|
320
|
+
span_store=span_store,
|
|
312
321
|
)
|
|
313
322
|
self.server = ThreadServer(
|
|
314
323
|
app=self.app,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Callable, Iterator, Mapping, Protocol
|
|
3
|
+
|
|
4
|
+
from opentelemetry.proto.trace.v1.trace_pb2 import TracesData
|
|
5
|
+
|
|
6
|
+
from phoenix.config import SpanStorageType
|
|
7
|
+
from phoenix.storage.span_store.text_file import TextFileSpanStoreImpl
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SpanStore(Protocol):
|
|
11
|
+
def save(self, req: TracesData) -> None: ...
|
|
12
|
+
|
|
13
|
+
def load(self) -> Iterator[TracesData]: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
SPAN_STORE_FACTORIES: Mapping[SpanStorageType, Callable[[Path], SpanStore]] = {
|
|
17
|
+
SpanStorageType.TEXT_FILES: TextFileSpanStoreImpl,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
__all__ = (
|
|
21
|
+
"SpanStore",
|
|
22
|
+
"SPAN_STORE_FACTORIES",
|
|
23
|
+
)
|
phoenix/trace/__init__.py
CHANGED
|
@@ -17,7 +17,14 @@ __all__ = [
|
|
|
17
17
|
|
|
18
18
|
@contextlib.contextmanager
|
|
19
19
|
def suppress_tracing() -> Iterator[None]:
|
|
20
|
-
"""
|
|
20
|
+
"""
|
|
21
|
+
Context manager to pause OpenTelemetry instrumentation.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
with suppress_tracing():
|
|
25
|
+
# No tracing will occur within this block
|
|
26
|
+
...
|
|
27
|
+
"""
|
|
21
28
|
try:
|
|
22
29
|
from opentelemetry.context import _SUPPRESS_INSTRUMENTATION_KEY, attach, detach, set_value
|
|
23
30
|
except ImportError:
|
phoenix/trace/dsl/helpers.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import List, Optional, Protocol, Union, cast
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
from openinference.semconv.trace import DocumentAttributes, SpanAttributes
|
|
5
5
|
|
|
6
|
+
from phoenix.config import get_env_project_name
|
|
6
7
|
from phoenix.trace.dsl import SpanQuery
|
|
7
8
|
|
|
8
9
|
DOCUMENT_CONTENT = DocumentAttributes.DOCUMENT_CONTENT
|
|
@@ -21,12 +22,14 @@ IS_RETRIEVER = "span_kind == 'RETRIEVER'"
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class CanQuerySpans(Protocol):
|
|
25
|
+
# Implemented by phoenix.session.client.Client
|
|
24
26
|
def query_spans(
|
|
25
|
-
self, *query: SpanQuery
|
|
27
|
+
self, *query: SpanQuery, project_name: Optional[str] = None
|
|
26
28
|
) -> Optional[Union[pd.DataFrame, List[pd.DataFrame]]]: ...
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
def get_retrieved_documents(obj: CanQuerySpans) -> pd.DataFrame:
|
|
31
|
+
def get_retrieved_documents(obj: CanQuerySpans, project_name: Optional[str] = None) -> pd.DataFrame:
|
|
32
|
+
project_name = project_name or get_env_project_name()
|
|
30
33
|
return cast(
|
|
31
34
|
pd.DataFrame,
|
|
32
35
|
obj.query_spans(
|
|
@@ -37,12 +40,14 @@ def get_retrieved_documents(obj: CanQuerySpans) -> pd.DataFrame:
|
|
|
37
40
|
RETRIEVAL_DOCUMENTS,
|
|
38
41
|
reference=DOCUMENT_CONTENT,
|
|
39
42
|
document_score=DOCUMENT_SCORE,
|
|
40
|
-
)
|
|
43
|
+
),
|
|
44
|
+
project_name=project_name,
|
|
41
45
|
),
|
|
42
46
|
)
|
|
43
47
|
|
|
44
48
|
|
|
45
|
-
def get_qa_with_reference(obj: CanQuerySpans) -> pd.DataFrame:
|
|
49
|
+
def get_qa_with_reference(obj: CanQuerySpans, project_name: Optional[str] = None) -> pd.DataFrame:
|
|
50
|
+
project_name = project_name or get_env_project_name()
|
|
46
51
|
return pd.concat(
|
|
47
52
|
cast(
|
|
48
53
|
List[pd.DataFrame],
|
|
@@ -55,6 +60,7 @@ def get_qa_with_reference(obj: CanQuerySpans) -> pd.DataFrame:
|
|
|
55
60
|
RETRIEVAL_DOCUMENTS,
|
|
56
61
|
reference=DOCUMENT_CONTENT,
|
|
57
62
|
),
|
|
63
|
+
project_name=project_name,
|
|
58
64
|
),
|
|
59
65
|
),
|
|
60
66
|
axis=1,
|
|
@@ -28,7 +28,8 @@ class LangChainInstrumentor(Instrumentor):
|
|
|
28
28
|
|
|
29
29
|
def instrument(self) -> None:
|
|
30
30
|
tracer_provider = trace_sdk.TracerProvider(
|
|
31
|
-
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()})
|
|
31
|
+
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
|
|
32
|
+
span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
|
|
32
33
|
)
|
|
33
34
|
tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
|
|
34
35
|
super().instrument(skip_dep_check=True, tracer_provider=tracer_provider)
|
|
@@ -95,7 +95,8 @@ class OpenInferenceTraceCallbackHandler(_OpenInferenceTraceCallbackHandler):
|
|
|
95
95
|
|
|
96
96
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
97
97
|
tracer_provider = trace_sdk.TracerProvider(
|
|
98
|
-
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()})
|
|
98
|
+
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
|
|
99
|
+
span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
|
|
99
100
|
)
|
|
100
101
|
tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
|
|
101
102
|
super().__init__(trace_api.get_tracer(__name__, __version__, tracer_provider))
|
|
@@ -23,7 +23,8 @@ class OpenAIInstrumentor(Instrumentor):
|
|
|
23
23
|
|
|
24
24
|
def instrument(self) -> None:
|
|
25
25
|
tracer_provider = trace_sdk.TracerProvider(
|
|
26
|
-
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()})
|
|
26
|
+
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
|
|
27
|
+
span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
|
|
27
28
|
)
|
|
28
29
|
tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
|
|
29
30
|
super().instrument(skip_dep_check=True, tracer_provider=tracer_provider)
|
phoenix/trace/projects.py
CHANGED
|
@@ -30,9 +30,19 @@ class using_project:
|
|
|
30
30
|
"""
|
|
31
31
|
A context manager that switches the project for all spans created within the context.
|
|
32
32
|
|
|
33
|
-
This is
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
This is intended for use in notebook environments where it's useful to be able to change the
|
|
34
|
+
project associated with spans on the fly.
|
|
35
|
+
|
|
36
|
+
Note: This should not be used in production environments or complex OpenTelemetry setups.
|
|
37
|
+
As dynamically modifying span resources in this way can lead to unexpected behavior.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
project_name (str): The project name to associate with spans created within the context.
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
with using_project('my_project'):
|
|
44
|
+
# Spans created here will be associated with 'my_project'
|
|
45
|
+
...
|
|
36
46
|
"""
|
|
37
47
|
|
|
38
48
|
def __init__(self, project_name: str) -> None:
|
|
@@ -5,6 +5,8 @@ from enum import Enum
|
|
|
5
5
|
from typing import Any, List
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
8
10
|
from phoenix.trace.schemas import (
|
|
9
11
|
Span,
|
|
10
12
|
SpanContext,
|
|
@@ -45,6 +47,12 @@ class SpanJSONEncoder(json.JSONEncoder):
|
|
|
45
47
|
}
|
|
46
48
|
elif isinstance(obj, SpanConversationAttributes):
|
|
47
49
|
return {"conversation_id": str(obj.conversation_id)}
|
|
50
|
+
elif isinstance(obj, np.ndarray):
|
|
51
|
+
return list(obj)
|
|
52
|
+
elif isinstance(obj, np.integer):
|
|
53
|
+
return int(obj)
|
|
54
|
+
elif isinstance(obj, np.floating):
|
|
55
|
+
return float(obj)
|
|
48
56
|
return super().default(obj)
|
|
49
57
|
|
|
50
58
|
|
phoenix/utilities/project.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Iterable
|
|
|
3
3
|
from openinference.semconv.resource import ResourceAttributes
|
|
4
4
|
from opentelemetry.proto.common.v1.common_pb2 import KeyValue
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
from phoenix.config import DEFAULT_PROJECT_NAME
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def get_project_name(attributes: Iterable[KeyValue]) -> str:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from phoenix.config import get_env_span_storage_type, get_storage_dir
|
|
4
|
+
from phoenix.core.traces import Traces
|
|
5
|
+
from phoenix.storage.span_store import SPAN_STORE_FACTORIES, SpanStore
|
|
6
|
+
from phoenix.trace.otel import decode
|
|
7
|
+
from phoenix.utilities.project import get_project_name
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_span_store() -> Optional[SpanStore]:
|
|
11
|
+
if span_store_type := get_env_span_storage_type():
|
|
12
|
+
span_store_factory = SPAN_STORE_FACTORIES[span_store_type]
|
|
13
|
+
return span_store_factory(get_storage_dir())
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_traces_data_from_store(traces: Traces, span_store: SpanStore) -> None:
|
|
18
|
+
for traces_data in span_store.load():
|
|
19
|
+
for resource_spans in traces_data.resource_spans:
|
|
20
|
+
project_name = get_project_name(resource_spans.resource.attributes)
|
|
21
|
+
for scope_span in resource_spans.scope_spans:
|
|
22
|
+
for span in scope_span.spans:
|
|
23
|
+
traces.put(decode(span), project_name=project_name)
|
phoenix/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "3.
|
|
1
|
+
__version__ = "3.15.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|