arize-phoenix 3.14.2__py3-none-any.whl → 3.15.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.

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
- Parameters
38
- ----------
39
- endpoint : str, optional
40
- Phoenix server endpoint, e.g. http://localhost:6006. If not provided, the
41
- endpoint will be inferred from the environment variables.
42
- use_active_session_if_available : bool, optional
43
- If px.active_session() is available in the same runtime, e.g. the same Jupyter
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={"content-type": "application/x-pandas-arrow"},
193
+ headers=headers,
153
194
  ).raise_for_status()
154
195
 
155
196
 
@@ -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
- """Context manager to pause OpenTelemetry instrumentation."""
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:
@@ -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,
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 useful for managing projects in notebook environments, however this should not be used
34
- in production environments or complicated OpenTelemetry setups, as dynamically modifying the
35
- span resource can lead to unexpected behavior.
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:
@@ -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
- DEFAULT_PROJECT_NAME = "default"
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.14.2"
1
+ __version__ = "3.15.1"
@@ -1,9 +0,0 @@
1
- from typing import Iterator, Protocol
2
-
3
- from opentelemetry.proto.trace.v1.trace_pb2 import TracesData
4
-
5
-
6
- class SpanStore(Protocol):
7
- def save(self, req: TracesData) -> None: ...
8
-
9
- def load(self) -> Iterator[TracesData]: ...
File without changes