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

Files changed (113) hide show
  1. {arize_phoenix-3.25.0.dist-info → arize_phoenix-4.0.1.dist-info}/METADATA +26 -4
  2. {arize_phoenix-3.25.0.dist-info → arize_phoenix-4.0.1.dist-info}/RECORD +80 -75
  3. phoenix/__init__.py +9 -5
  4. phoenix/config.py +109 -53
  5. phoenix/datetime_utils.py +18 -1
  6. phoenix/db/README.md +25 -0
  7. phoenix/db/__init__.py +4 -0
  8. phoenix/db/alembic.ini +119 -0
  9. phoenix/db/bulk_inserter.py +206 -0
  10. phoenix/db/engines.py +152 -0
  11. phoenix/db/helpers.py +47 -0
  12. phoenix/db/insertion/evaluation.py +209 -0
  13. phoenix/db/insertion/helpers.py +51 -0
  14. phoenix/db/insertion/span.py +142 -0
  15. phoenix/db/migrate.py +71 -0
  16. phoenix/db/migrations/env.py +121 -0
  17. phoenix/db/migrations/script.py.mako +26 -0
  18. phoenix/db/migrations/versions/cf03bd6bae1d_init.py +280 -0
  19. phoenix/db/models.py +371 -0
  20. phoenix/exceptions.py +5 -1
  21. phoenix/server/api/context.py +40 -3
  22. phoenix/server/api/dataloaders/__init__.py +97 -0
  23. phoenix/server/api/dataloaders/cache/__init__.py +3 -0
  24. phoenix/server/api/dataloaders/cache/two_tier_cache.py +67 -0
  25. phoenix/server/api/dataloaders/document_evaluation_summaries.py +152 -0
  26. phoenix/server/api/dataloaders/document_evaluations.py +37 -0
  27. phoenix/server/api/dataloaders/document_retrieval_metrics.py +98 -0
  28. phoenix/server/api/dataloaders/evaluation_summaries.py +151 -0
  29. phoenix/server/api/dataloaders/latency_ms_quantile.py +198 -0
  30. phoenix/server/api/dataloaders/min_start_or_max_end_times.py +93 -0
  31. phoenix/server/api/dataloaders/record_counts.py +125 -0
  32. phoenix/server/api/dataloaders/span_descendants.py +64 -0
  33. phoenix/server/api/dataloaders/span_evaluations.py +37 -0
  34. phoenix/server/api/dataloaders/token_counts.py +138 -0
  35. phoenix/server/api/dataloaders/trace_evaluations.py +37 -0
  36. phoenix/server/api/input_types/SpanSort.py +138 -68
  37. phoenix/server/api/routers/v1/__init__.py +11 -0
  38. phoenix/server/api/routers/v1/evaluations.py +275 -0
  39. phoenix/server/api/routers/v1/spans.py +126 -0
  40. phoenix/server/api/routers/v1/traces.py +82 -0
  41. phoenix/server/api/schema.py +112 -48
  42. phoenix/server/api/types/DocumentEvaluationSummary.py +1 -1
  43. phoenix/server/api/types/Evaluation.py +29 -12
  44. phoenix/server/api/types/EvaluationSummary.py +29 -44
  45. phoenix/server/api/types/MimeType.py +2 -2
  46. phoenix/server/api/types/Model.py +9 -9
  47. phoenix/server/api/types/Project.py +240 -171
  48. phoenix/server/api/types/Span.py +87 -131
  49. phoenix/server/api/types/Trace.py +29 -20
  50. phoenix/server/api/types/pagination.py +151 -10
  51. phoenix/server/app.py +263 -35
  52. phoenix/server/grpc_server.py +93 -0
  53. phoenix/server/main.py +75 -60
  54. phoenix/server/openapi/docs.py +218 -0
  55. phoenix/server/prometheus.py +23 -7
  56. phoenix/server/static/index.js +662 -643
  57. phoenix/server/telemetry.py +68 -0
  58. phoenix/services.py +4 -0
  59. phoenix/session/client.py +34 -30
  60. phoenix/session/data_extractor.py +8 -3
  61. phoenix/session/session.py +176 -155
  62. phoenix/settings.py +13 -0
  63. phoenix/trace/attributes.py +349 -0
  64. phoenix/trace/dsl/README.md +116 -0
  65. phoenix/trace/dsl/filter.py +660 -192
  66. phoenix/trace/dsl/helpers.py +24 -5
  67. phoenix/trace/dsl/query.py +562 -185
  68. phoenix/trace/fixtures.py +69 -7
  69. phoenix/trace/otel.py +44 -200
  70. phoenix/trace/schemas.py +14 -8
  71. phoenix/trace/span_evaluations.py +5 -2
  72. phoenix/utilities/__init__.py +0 -26
  73. phoenix/utilities/span_store.py +0 -23
  74. phoenix/version.py +1 -1
  75. phoenix/core/project.py +0 -773
  76. phoenix/core/traces.py +0 -96
  77. phoenix/datasets/dataset.py +0 -214
  78. phoenix/datasets/fixtures.py +0 -24
  79. phoenix/datasets/schema.py +0 -31
  80. phoenix/experimental/evals/__init__.py +0 -73
  81. phoenix/experimental/evals/evaluators.py +0 -413
  82. phoenix/experimental/evals/functions/__init__.py +0 -4
  83. phoenix/experimental/evals/functions/classify.py +0 -453
  84. phoenix/experimental/evals/functions/executor.py +0 -353
  85. phoenix/experimental/evals/functions/generate.py +0 -138
  86. phoenix/experimental/evals/functions/processing.py +0 -76
  87. phoenix/experimental/evals/models/__init__.py +0 -14
  88. phoenix/experimental/evals/models/anthropic.py +0 -175
  89. phoenix/experimental/evals/models/base.py +0 -170
  90. phoenix/experimental/evals/models/bedrock.py +0 -221
  91. phoenix/experimental/evals/models/litellm.py +0 -134
  92. phoenix/experimental/evals/models/openai.py +0 -453
  93. phoenix/experimental/evals/models/rate_limiters.py +0 -246
  94. phoenix/experimental/evals/models/vertex.py +0 -173
  95. phoenix/experimental/evals/models/vertexai.py +0 -186
  96. phoenix/experimental/evals/retrievals.py +0 -96
  97. phoenix/experimental/evals/templates/__init__.py +0 -50
  98. phoenix/experimental/evals/templates/default_templates.py +0 -472
  99. phoenix/experimental/evals/templates/template.py +0 -195
  100. phoenix/experimental/evals/utils/__init__.py +0 -172
  101. phoenix/experimental/evals/utils/threads.py +0 -27
  102. phoenix/server/api/routers/evaluation_handler.py +0 -110
  103. phoenix/server/api/routers/span_handler.py +0 -70
  104. phoenix/server/api/routers/trace_handler.py +0 -60
  105. phoenix/storage/span_store/__init__.py +0 -23
  106. phoenix/storage/span_store/text_file.py +0 -85
  107. phoenix/trace/dsl/missing.py +0 -60
  108. {arize_phoenix-3.25.0.dist-info → arize_phoenix-4.0.1.dist-info}/WHEEL +0 -0
  109. {arize_phoenix-3.25.0.dist-info → arize_phoenix-4.0.1.dist-info}/licenses/IP_NOTICE +0 -0
  110. {arize_phoenix-3.25.0.dist-info → arize_phoenix-4.0.1.dist-info}/licenses/LICENSE +0 -0
  111. /phoenix/{datasets → db/insertion}/__init__.py +0 -0
  112. /phoenix/{experimental → db/migrations}/__init__.py +0 -0
  113. /phoenix/{storage → server/openapi}/__init__.py +0 -0
@@ -0,0 +1,68 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from phoenix.config import (
5
+ ENV_PHOENIX_SERVER_INSTRUMENTATION_OTLP_TRACE_COLLECTOR_GRPC_ENDPOINT,
6
+ ENV_PHOENIX_SERVER_INSTRUMENTATION_OTLP_TRACE_COLLECTOR_HTTP_ENDPOINT,
7
+ )
8
+
9
+ if TYPE_CHECKING:
10
+ from opentelemetry.trace import TracerProvider
11
+ from logging import getLogger
12
+
13
+ logger = getLogger(__name__)
14
+
15
+
16
+ def normalize_http_collector_endpoint(endpoint: str) -> str:
17
+ normalized_endpoint = endpoint
18
+ if not normalized_endpoint.startswith("http://") and not normalized_endpoint.startswith(
19
+ "https://"
20
+ ):
21
+ logger.warning(
22
+ "HTTP collector endpoint should include the protocol (http:// or https://)."
23
+ "Assuming http."
24
+ )
25
+ # assume http if no protocol is provided
26
+ normalized_endpoint = f"http://{endpoint}"
27
+ if normalized_endpoint.endswith("/v1/traces"):
28
+ logger.warning(
29
+ "HTTP collector endpoint should not include the /v1/traces path. Removing it."
30
+ )
31
+ # remove the /v1/traces path
32
+ normalized_endpoint = normalized_endpoint[: -len("/v1/traces")]
33
+ # remove trailing slashes
34
+ normalized_endpoint = normalized_endpoint.rstrip("/")
35
+ return normalized_endpoint
36
+
37
+
38
+ def initialize_opentelemetry_tracer_provider() -> "TracerProvider":
39
+ logger.info("Initializing OpenTelemetry tracer provider")
40
+ from opentelemetry.sdk import trace as trace_sdk
41
+ from opentelemetry.sdk.resources import Resource
42
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
43
+ from opentelemetry.semconv.resource import ResourceAttributes
44
+
45
+ tracer_provider = trace_sdk.TracerProvider(
46
+ resource=Resource(attributes={ResourceAttributes.SERVICE_NAME: "arize-phoenix-server"})
47
+ )
48
+ if http_endpoint := os.getenv(
49
+ ENV_PHOENIX_SERVER_INSTRUMENTATION_OTLP_TRACE_COLLECTOR_HTTP_ENDPOINT
50
+ ):
51
+ logger.info(f"Using HTTP collector endpoint: {http_endpoint}")
52
+ http_endpoint = normalize_http_collector_endpoint(http_endpoint) + "/v1/traces"
53
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
54
+ OTLPSpanExporter as HttpExporter,
55
+ )
56
+
57
+ tracer_provider.add_span_processor(BatchSpanProcessor(HttpExporter(http_endpoint)))
58
+ if grpc_endpoint := os.getenv(
59
+ ENV_PHOENIX_SERVER_INSTRUMENTATION_OTLP_TRACE_COLLECTOR_GRPC_ENDPOINT
60
+ ):
61
+ logger.info(f"Using gRPC collector endpoint: {grpc_endpoint}")
62
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
63
+ OTLPSpanExporter as GrpcExporter,
64
+ )
65
+
66
+ tracer_provider.add_span_processor(BatchSpanProcessor(GrpcExporter(grpc_endpoint)))
67
+ logger.info("🔭 OpenTelemetry tracer provider initialized")
68
+ return tracer_provider
phoenix/services.py CHANGED
@@ -107,6 +107,7 @@ class AppService(Service):
107
107
 
108
108
  def __init__(
109
109
  self,
110
+ database_url: str,
110
111
  export_path: Path,
111
112
  host: str,
112
113
  port: int,
@@ -117,6 +118,7 @@ class AppService(Service):
117
118
  corpus_dataset_name: Optional[str],
118
119
  trace_dataset_name: Optional[str],
119
120
  ):
121
+ self.database_url = database_url
120
122
  self.export_path = export_path
121
123
  self.host = host
122
124
  self.port = port
@@ -133,6 +135,8 @@ class AppService(Service):
133
135
  command = [
134
136
  sys.executable,
135
137
  "main.py",
138
+ "--database-url",
139
+ self.database_url,
136
140
  "--export_path",
137
141
  str(self.export_path),
138
142
  "--host",
phoenix/session/client.py CHANGED
@@ -3,7 +3,7 @@ import logging
3
3
  import weakref
4
4
  from datetime import datetime
5
5
  from io import BytesIO
6
- from typing import List, Optional, Union, cast
6
+ from typing import Any, List, Optional, Union, cast
7
7
  from urllib.parse import urljoin
8
8
 
9
9
  import pandas as pd
@@ -15,14 +15,14 @@ from opentelemetry.proto.trace.v1.trace_pb2 import ResourceSpans, ScopeSpans
15
15
  from pyarrow import ArrowInvalid
16
16
  from requests import Session
17
17
 
18
- import phoenix as px
19
18
  from phoenix.config import (
20
19
  get_env_collector_endpoint,
21
20
  get_env_host,
22
21
  get_env_port,
23
22
  get_env_project_name,
24
23
  )
25
- from phoenix.session.data_extractor import TraceDataExtractor
24
+ from phoenix.datetime_utils import normalize_datetime
25
+ from phoenix.session.data_extractor import DEFAULT_SPAN_LIMIT, TraceDataExtractor
26
26
  from phoenix.trace import Evaluations, TraceDataset
27
27
  from phoenix.trace.dsl import SpanQuery
28
28
  from phoenix.trace.otel import encode_span_to_otlp
@@ -35,7 +35,8 @@ class Client(TraceDataExtractor):
35
35
  self,
36
36
  *,
37
37
  endpoint: Optional[str] = None,
38
- use_active_session_if_available: bool = True,
38
+ warn_if_server_not_running: bool = True,
39
+ **kwargs: Any, # for backward-compatibility
39
40
  ):
40
41
  """
41
42
  Client for connecting to a Phoenix server.
@@ -43,12 +44,14 @@ class Client(TraceDataExtractor):
43
44
  Args:
44
45
  endpoint (str, optional): Phoenix server endpoint, e.g. http://localhost:6006. If not
45
46
  provided, the endpoint will be inferred from the environment variables.
46
- use_active_session_if_available (bool, optional): If px.active_session() is available
47
- in the same runtime, e.g. the same Jupyter notebook, delegate the request to the
48
- active session instead of making HTTP requests. This argument is set to False if
49
- endpoint is provided explicitly.
50
47
  """
51
- self._use_active_session_if_available = use_active_session_if_available and not endpoint
48
+ if kwargs.pop("use_active_session_if_available", None) is not None:
49
+ print(
50
+ "`use_active_session_if_available` is deprecated "
51
+ "and will be removed in the future."
52
+ )
53
+ if kwargs:
54
+ raise TypeError(f"Unexpected keyword arguments: {', '.join(kwargs)}")
52
55
  host = get_env_host()
53
56
  if host == "0.0.0.0":
54
57
  host = "127.0.0.1"
@@ -57,16 +60,19 @@ class Client(TraceDataExtractor):
57
60
  )
58
61
  self._session = Session()
59
62
  weakref.finalize(self, self._session.close)
60
- if not (self._use_active_session_if_available and px.active_session()):
63
+ if warn_if_server_not_running:
61
64
  self._warn_if_phoenix_is_not_running()
62
65
 
63
66
  def query_spans(
64
67
  self,
65
68
  *queries: SpanQuery,
66
69
  start_time: Optional[datetime] = None,
67
- stop_time: Optional[datetime] = None,
70
+ end_time: Optional[datetime] = None,
71
+ limit: Optional[int] = DEFAULT_SPAN_LIMIT,
68
72
  root_spans_only: Optional[bool] = None,
69
73
  project_name: Optional[str] = None,
74
+ # Deprecated
75
+ stop_time: Optional[datetime] = None,
70
76
  ) -> Optional[Union[pd.DataFrame, List[pd.DataFrame]]]:
71
77
  """
72
78
  Queries spans from the Phoenix server or active session based on specified criteria.
@@ -74,7 +80,7 @@ class Client(TraceDataExtractor):
74
80
  Args:
75
81
  queries (SpanQuery): One or more SpanQuery objects defining the query criteria.
76
82
  start_time (datetime, optional): The start time for the query range. Default None.
77
- stop_time (datetime, optional): The stop time for the query range. Default None.
83
+ end_time (datetime, optional): The end time for the query range. Default None.
78
84
  root_spans_only (bool, optional): If True, only root spans are returned. Default None.
79
85
  project_name (str, optional): The project name to query spans for. This can be set
80
86
  using environment variables. If not provided, falls back to the default project.
@@ -86,22 +92,21 @@ class Client(TraceDataExtractor):
86
92
  project_name = project_name or get_env_project_name()
87
93
  if not queries:
88
94
  queries = (SpanQuery(),)
89
- if self._use_active_session_if_available and (session := px.active_session()):
90
- return session.query_spans(
91
- *queries,
92
- start_time=start_time,
93
- stop_time=stop_time,
94
- root_spans_only=root_spans_only,
95
- project_name=project_name,
95
+ if stop_time is not None:
96
+ # Deprecated. Raise a warning
97
+ logger.warning(
98
+ "stop_time is deprecated. Use end_time instead.",
96
99
  )
97
- response = self._session.get(
100
+ end_time = end_time or stop_time
101
+ response = self._session.post(
98
102
  url=urljoin(self._base_url, "/v1/spans"),
103
+ params={"project-name": project_name},
99
104
  json={
100
105
  "queries": [q.to_dict() for q in queries],
101
- "start_time": _to_iso_format(start_time),
102
- "stop_time": _to_iso_format(stop_time),
106
+ "start_time": _to_iso_format(normalize_datetime(start_time)),
107
+ "end_time": _to_iso_format(normalize_datetime(end_time)),
108
+ "limit": limit,
103
109
  "root_spans_only": root_spans_only,
104
- "project_name": project_name,
105
110
  },
106
111
  )
107
112
  if response.status_code == 404:
@@ -140,11 +145,9 @@ class Client(TraceDataExtractor):
140
145
  empty list if no evaluations are found.
141
146
  """
142
147
  project_name = project_name or get_env_project_name()
143
- if self._use_active_session_if_available and (session := px.active_session()):
144
- return session.get_evaluations(project_name=project_name)
145
148
  response = self._session.get(
146
149
  urljoin(self._base_url, "/v1/evaluations"),
147
- json={"project_name": project_name},
150
+ params={"project-name": project_name},
148
151
  )
149
152
  if response.status_code == 404:
150
153
  logger.info("No evaluations found.")
@@ -171,7 +174,7 @@ class Client(TraceDataExtractor):
171
174
  f"with `import phoenix as px; px.launch_app()`"
172
175
  )
173
176
 
174
- def log_evaluations(self, *evals: Evaluations, project_name: Optional[str] = None) -> None:
177
+ def log_evaluations(self, *evals: Evaluations, **kwargs: Any) -> None:
175
178
  """
176
179
  Logs evaluation data to the Phoenix server.
177
180
 
@@ -184,13 +187,14 @@ class Client(TraceDataExtractor):
184
187
  Returns:
185
188
  None
186
189
  """
187
- project_name = project_name or get_env_project_name()
190
+ if kwargs.pop("project_name", None) is not None:
191
+ print("Keyword argument `project_name` is no longer necessary and is ignored.")
192
+ if kwargs:
193
+ raise TypeError(f"Unexpected keyword arguments: {', '.join(kwargs)}")
188
194
  for evaluation in evals:
189
195
  table = evaluation.to_pyarrow_table()
190
196
  sink = pa.BufferOutputStream()
191
197
  headers = {"content-type": "application/x-pandas-arrow"}
192
- if project_name:
193
- headers["project-name"] = project_name
194
198
  with pa.ipc.new_stream(sink, table.schema) as writer:
195
199
  writer.write_table(table)
196
200
  self._session.post(
@@ -8,6 +8,8 @@ from phoenix.trace import Evaluations
8
8
  from phoenix.trace.dsl import SpanQuery
9
9
  from phoenix.trace.trace_dataset import TraceDataset
10
10
 
11
+ DEFAULT_SPAN_LIMIT = 1000
12
+
11
13
 
12
14
  class TraceDataExtractor(ABC):
13
15
  """
@@ -20,7 +22,8 @@ class TraceDataExtractor(ABC):
20
22
  self,
21
23
  *queries: SpanQuery,
22
24
  start_time: Optional[datetime] = None,
23
- stop_time: Optional[datetime] = None,
25
+ end_time: Optional[datetime] = None,
26
+ limit: Optional[int] = DEFAULT_SPAN_LIMIT,
24
27
  root_spans_only: Optional[bool] = None,
25
28
  project_name: Optional[str] = None,
26
29
  ) -> Optional[Union[pd.DataFrame, List[pd.DataFrame]]]: ...
@@ -30,7 +33,8 @@ class TraceDataExtractor(ABC):
30
33
  filter_condition: Optional[str] = None,
31
34
  *,
32
35
  start_time: Optional[datetime] = None,
33
- stop_time: Optional[datetime] = None,
36
+ end_time: Optional[datetime] = None,
37
+ limit: Optional[int] = DEFAULT_SPAN_LIMIT,
34
38
  root_spans_only: Optional[bool] = None,
35
39
  project_name: Optional[str] = None,
36
40
  ) -> Optional[pd.DataFrame]:
@@ -39,7 +43,8 @@ class TraceDataExtractor(ABC):
39
43
  self.query_spans(
40
44
  SpanQuery().where(filter_condition or ""),
41
45
  start_time=start_time,
42
- stop_time=stop_time,
46
+ end_time=end_time,
47
+ limit=limit,
43
48
  root_spans_only=root_spans_only,
44
49
  project_name=project_name,
45
50
  ),