arize-phoenix 2.4.1rc4__py3-none-any.whl → 2.4.1rc6__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arize-phoenix
3
- Version: 2.4.1rc4
3
+ Version: 2.4.1rc6
4
4
  Summary: ML Observability in your notebook
5
5
  Project-URL: Documentation, https://docs.arize.com/phoenix/
6
6
  Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
@@ -3,8 +3,8 @@ phoenix/config.py,sha256=wYmvT1I3wad8YIunbsiJ0nBu4-W8pUZwwbs5CxJKRs8,2442
3
3
  phoenix/datetime_utils.py,sha256=D955QLrkgrrSdUM6NyqbCeAu2SMsjhR5rHVQEsVUdng,2773
4
4
  phoenix/exceptions.py,sha256=igIWGAg3m8jm5YwQDeCY1p8ml_60A7zaGVXJ1yZhY9s,44
5
5
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
- phoenix/services.py,sha256=slL4Uu___QQSKEssgD738-WAld-kzVQnpW92uKLxV4E,4886
7
- phoenix/version.py,sha256=jQJy8hLBFCsN7f2COT6xTJBYFtZehVyfl5Pb66EeJzU,25
6
+ phoenix/services.py,sha256=f6AeyKTuOpy9RCcTCjVH3gx5nYZhbTMFOuv1WSUOB5o,4992
7
+ phoenix/version.py,sha256=PBfHg55LBkQ021mwCxcQp5JEkKfbXnP1GrPdM50Lifo,25
8
8
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
10
10
  phoenix/core/evals.py,sha256=OrHeYlh804rpcZIXTA6kan2mzSZMfgpphNNQdPMpNoM,7597
@@ -55,11 +55,11 @@ phoenix/pointcloud/pointcloud.py,sha256=ms-h1FLC0xXb3sk256zpSuZQDE2hdOAJzRNBklP0
55
55
  phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
56
56
  phoenix/pointcloud/umap_parameters.py,sha256=lJsEOrbSuSiqI7g4Yt6xj7kgYxEqoep4ZHWLr6VWBqw,1760
57
57
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- phoenix/server/app.py,sha256=ZjVzQ4PELR0m6vCRQ7exzhb9Q1LHyu6cl3ttkNsPrgE,8110
58
+ phoenix/server/app.py,sha256=-Xn6BTdQsJ_VAAOWwiLQr_ZeyfOBSO_0cFyY7ewkqFM,7802
59
59
  phoenix/server/evaluation_handler.py,sha256=HzaoD8Cv9HbEdd0nYSTZoakKsE8Ic5lVjeuBh0vnhoA,1554
60
60
  phoenix/server/main.py,sha256=ZWV_UQOE2c5Gx4x_2UQiZIww3JMRWosza5BbzstTUsg,6814
61
61
  phoenix/server/span_handler.py,sha256=reYUDaN5bavSFjEiSfvYyAG_mpJs6S3iB-RNCkZrSUU,1295
62
- phoenix/server/thread_server.py,sha256=a9Vnzc69ZLqJbI_FUSOY3eeuYCiCq6aprPj2gS_RB-M,2097
62
+ phoenix/server/thread_server.py,sha256=z6VwimjlHEsOavlybcQFVWubZvHDGYOH7p61nscC0lU,2164
63
63
  phoenix/server/trace_handler.py,sha256=pXanrp9L21Mh7MnyJbj202NJ-Rn4bCjG0oL4DtdKcls,2074
64
64
  phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
65
  phoenix/server/api/context.py,sha256=02vRgyLFpDCmh97QwsjWD5cdNZkoCUtDPPs1YItbdbI,583
@@ -130,10 +130,10 @@ phoenix/server/static/index.js,sha256=dJKCIzqIRBf_IKfXUbnSZnegpgnfMV3wsnFGXWV9Ie
130
130
  phoenix/server/static/index.js.map,sha256=Pe5FBAB7cKnRik9CrPFsP4AHe3MR3LUI7OShR2pA5ys,15465655
131
131
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
132
132
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
- phoenix/server/templates/index.html,sha256=6Zt_ouVbwQ8fDIQgVqlumu-SBwbIFkbcGaDVHgrlrNY,1868
133
+ phoenix/server/templates/index.html,sha256=7N-1rUz82nvblYndZLdZ_VdIOSKD5wGq4LyO5Cs-P5w,1916
134
134
  phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
135
  phoenix/session/evaluation.py,sha256=s5OivScAMSj8qfU4IexpmbyKvcGBj_nt-GP_13_o-iY,4843
136
- phoenix/session/session.py,sha256=ZQaakc5qhiJOzdyEMKAbXBx3UCPTcufytYXDhbctmJo,18941
136
+ phoenix/session/session.py,sha256=nZxE2CCip6vgUU9KyzaxQmSLF3v4ywYGLWmCUR1K5no,20104
137
137
  phoenix/trace/__init__.py,sha256=4d_MqzUIFmlY9WWcFeTONJ4xL5mPGoWZaPM2TJ0ZDBQ,266
138
138
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
139
139
  phoenix/trace/exporter.py,sha256=z3xrGJhIRh7XMy4Q1FkR3KmFZym-GX0XxLTZ6eSnN0Q,4347
@@ -167,8 +167,8 @@ phoenix/trace/v1/evaluation_pb2.pyi,sha256=cCbbx06gwQmaH14s3J1X25TtaARh-k1abbxQd
167
167
  phoenix/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
168
  phoenix/utilities/error_handling.py,sha256=7b5rpGFj9EWZ8yrZK1IHvxB89suWk3lggDayUQcvZds,1946
169
169
  phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,222
170
- arize_phoenix-2.4.1rc4.dist-info/METADATA,sha256=4ex6gs_0C1rsR_hqChGnoTXjsHj2092ltjx0TvqCV9A,26482
171
- arize_phoenix-2.4.1rc4.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
172
- arize_phoenix-2.4.1rc4.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
173
- arize_phoenix-2.4.1rc4.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
174
- arize_phoenix-2.4.1rc4.dist-info/RECORD,,
170
+ arize_phoenix-2.4.1rc6.dist-info/METADATA,sha256=QIZlIFPN4oM5mMXyYkwIALOztVAI59q5eACLivAVnQw,26482
171
+ arize_phoenix-2.4.1rc6.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
172
+ arize_phoenix-2.4.1rc6.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
173
+ arize_phoenix-2.4.1rc6.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
174
+ arize_phoenix-2.4.1rc6.dist-info/RECORD,,
phoenix/server/app.py CHANGED
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from typing import Any, NamedTuple, Optional, Union
5
5
 
6
6
  from starlette.applications import Starlette
7
- from starlette.datastructures import QueryParams
7
+ from starlette.datastructures import Headers, QueryParams
8
8
  from starlette.endpoints import HTTPEndpoint
9
9
  from starlette.exceptions import HTTPException
10
10
  from starlette.middleware import Middleware
@@ -35,8 +35,6 @@ logger = logging.getLogger(__name__)
35
35
 
36
36
  templates = Jinja2Templates(directory=SERVER_DIR / "templates")
37
37
 
38
- _databricks_basename_pattern = r"https://.*?\.com(/driver-proxy/o/\d+/\d+-\d+-\w+/\d+/)"
39
-
40
38
 
41
39
  class AppConfig(NamedTuple):
42
40
  has_corpus: bool
@@ -53,29 +51,22 @@ def _is_databricks(url: str) -> bool:
53
51
  return "databricks" in url
54
52
 
55
53
 
56
- def _get_databricks_basename(url: str) -> str:
57
- match = re.search(_databricks_basename_pattern, url)
58
- if match is None:
59
- raise ValueError("Could not decipher the basename in Databricks URL")
60
- return match.group(1)
61
-
62
-
63
54
  def _get_basename(request: Request) -> str:
64
55
  """
65
56
  Determine the basename for the API and static content.
66
57
  If the server is running in a hosted notebook, the base URL
67
58
  must be configured to point to the hosted notebook's proxy.
68
59
  """
69
- url = str(request.url)
70
- if _is_sagemaker(url):
60
+ if _is_sagemaker(str(request.url)):
71
61
  # Sagemaker sets up a proxy at /proxy/6006
72
62
  # NB: this is the only port that is open
73
63
  return "/proxy/6006"
74
- if _is_databricks(url):
75
- # For Databricks, the basename goes from /driver-proxy/ to the
76
- # end of the URL where there is a port number
77
- # For example: /driver-proxy/o/1018391329803962/0112-220452-osc8oiar/6007/
78
- return _get_databricks_basename(url)
64
+ if _is_databricks(request.headers.get("referer", "")):
65
+ # NG: For Databricks, the request.url is the IP address,
66
+ # not the DNS address, so we must rely on the referer
67
+
68
+ # For Databricks, we set the root_path to the basename
69
+ return request.scope.get("root_path", "")
79
70
  # URL is relative to / for colab and local
80
71
  return ""
81
72
 
@@ -107,9 +98,9 @@ class Static(StaticFiles):
107
98
  "n_neighbors": self._app_config.n_neighbors,
108
99
  "n_samples": self._app_config.n_samples,
109
100
  "basename": _get_basename(request),
110
- "path": request.headers.get(":path:", ""),
111
- "authority": request.headers.get(":authority:", ""),
112
101
  "request": request,
102
+ "referer": request.headers.get("referer", ""),
103
+ "root_path": request.scope.get("root_path", ""),
113
104
  },
114
105
  )
115
106
  except Exception as e:
@@ -22,8 +22,9 @@
22
22
  <div id="root"></div>
23
23
  // Request Info:
24
24
  // URL: {{request.url}}
25
- // Path: {{path}}
26
- // authority: {{authority}}
25
+ // Base URL: {{request.base_url}}
26
+ // Headers: {{request.headers}}
27
+ // Referer: {{referer}}
27
28
  <script>(function() {
28
29
  Object.defineProperty(window, "Config", {
29
30
  // Place any server-side injected config here
@@ -2,7 +2,7 @@ import asyncio
2
2
  import logging
3
3
  from threading import Thread
4
4
  from time import sleep, time
5
- from typing import Generator
5
+ from typing import Generator, Optional
6
6
 
7
7
  from starlette.applications import Starlette
8
8
  from uvicorn import Config, Server
@@ -27,6 +27,7 @@ class ThreadServer(Server):
27
27
  app: Starlette,
28
28
  host: str,
29
29
  port: int,
30
+ root_path: str,
30
31
  ) -> None:
31
32
  # Must use asyncio loop if nest_asyncio is applied
32
33
  # Otherwise the app crashes when the server is run in a thread
@@ -35,6 +36,7 @@ class ThreadServer(Server):
35
36
  app=app,
36
37
  host=host,
37
38
  port=port,
39
+ root_path=root_path,
38
40
  # TODO: save logs to file
39
41
  log_level=logging.ERROR,
40
42
  loop=loop,
phoenix/services.py CHANGED
@@ -110,6 +110,7 @@ class AppService(Service):
110
110
  export_path: Path,
111
111
  host: str,
112
112
  port: int,
113
+ root_path: str,
113
114
  primary_dataset_name: str,
114
115
  umap_params: str,
115
116
  reference_dataset_name: Optional[str],
@@ -119,6 +120,7 @@ class AppService(Service):
119
120
  self.export_path = export_path
120
121
  self.host = host
121
122
  self.port = port
123
+ self.root_path = root_path # TODO(mikeldking): Add support for root_path
122
124
  self.__primary_dataset_name = primary_dataset_name
123
125
  self.__umap_params = umap_params
124
126
  self.__reference_dataset_name = reference_dataset_name
@@ -13,6 +13,7 @@ from typing import (
13
13
  Iterable,
14
14
  List,
15
15
  Mapping,
16
+ NamedTuple,
16
17
  Optional,
17
18
  Set,
18
19
  Union,
@@ -96,6 +97,7 @@ class Session(ABC):
96
97
  default_umap_parameters: Optional[Mapping[str, Any]] = None,
97
98
  host: Optional[str] = None,
98
99
  port: Optional[int] = None,
100
+ root_path: Optional[str] = None,
99
101
  notebook_env: Optional[NotebookEnvironment] = None,
100
102
  ):
101
103
  self.primary_dataset = primary_dataset
@@ -125,6 +127,7 @@ class Session(ABC):
125
127
 
126
128
  self.host = host or get_env_host()
127
129
  self.port = port or get_env_port()
130
+ self.root_path = root_path or ""
128
131
  self.temp_dir = TemporaryDirectory()
129
132
  self.export_path = Path(self.temp_dir.name) / "exports"
130
133
  self.export_path.mkdir(parents=True, exist_ok=True)
@@ -225,6 +228,7 @@ class ProcessSession(Session):
225
228
  default_umap_parameters: Optional[Mapping[str, Any]] = None,
226
229
  host: Optional[str] = None,
227
230
  port: Optional[int] = None,
231
+ root_path: Optional[str] = None,
228
232
  notebook_env: Optional[NotebookEnvironment] = None,
229
233
  ) -> None:
230
234
  super().__init__(
@@ -235,6 +239,7 @@ class ProcessSession(Session):
235
239
  default_umap_parameters=default_umap_parameters,
236
240
  host=host,
237
241
  port=port,
242
+ root_path=root_path,
238
243
  notebook_env=notebook_env,
239
244
  )
240
245
  primary_dataset.to_disc()
@@ -254,6 +259,7 @@ class ProcessSession(Session):
254
259
  self.export_path,
255
260
  self.host,
256
261
  self.port,
262
+ self.root_path,
257
263
  self.primary_dataset.name,
258
264
  umap_params_str,
259
265
  reference_dataset_name=(
@@ -286,6 +292,7 @@ class ThreadSession(Session):
286
292
  default_umap_parameters: Optional[Mapping[str, Any]] = None,
287
293
  host: Optional[str] = None,
288
294
  port: Optional[int] = None,
295
+ root_path: Optional[str] = None,
289
296
  notebook_env: Optional[NotebookEnvironment] = None,
290
297
  ):
291
298
  super().__init__(
@@ -296,6 +303,7 @@ class ThreadSession(Session):
296
303
  default_umap_parameters=default_umap_parameters,
297
304
  host=host,
298
305
  port=port,
306
+ root_path=root_path,
299
307
  notebook_env=notebook_env,
300
308
  )
301
309
  # Initialize an app service that keeps the server running
@@ -311,6 +319,7 @@ class ThreadSession(Session):
311
319
  app=self.app,
312
320
  host=self.host,
313
321
  port=self.port,
322
+ root_path=self.root_path,
314
323
  ).run_in_thread()
315
324
  # start the server
316
325
  self.server_thread = next(self.server)
@@ -401,6 +410,7 @@ def launch_app(
401
410
 
402
411
  host = host or get_env_host()
403
412
  port = port or get_env_port()
413
+ root_path = _get_root_path(nb_env, port)
404
414
 
405
415
  if run_in_thread:
406
416
  _session = ThreadSession(
@@ -411,6 +421,7 @@ def launch_app(
411
421
  default_umap_parameters,
412
422
  host=host,
413
423
  port=port,
424
+ root_path=root_path,
414
425
  notebook_env=nb_env,
415
426
  )
416
427
  # TODO: catch exceptions from thread
@@ -471,7 +482,8 @@ def _get_url(host: str, port: int, notebook_env: NotebookEnvironment) -> str:
471
482
  # NB: Sagemaker notebooks only work with port 6006 - which is used by tensorboard
472
483
  return f"{_get_sagemaker_notebook_base_url()}/proxy/{port}/"
473
484
  if notebook_env == NotebookEnvironment.DATABRICKS:
474
- return f"{_get_databricks_notebook_base_url()}/{port}/"
485
+ context = _get_databricks_context()
486
+ return f"{_get_databricks_notebook_base_url(context)}/{port}/"
475
487
  return f"http://{host}:{port}/"
476
488
 
477
489
 
@@ -552,9 +564,26 @@ def _get_sagemaker_notebook_base_url() -> str:
552
564
  return f"https://{notebook_instance_name}.notebook.{region}.sagemaker.aws"
553
565
 
554
566
 
555
- def _get_databricks_notebook_base_url() -> str:
567
+ def _get_root_path(environment: Optional[NotebookEnvironment], port: int) -> str:
556
568
  """
557
- Returns base url of the databricks notebook by parsing the tags
569
+ Returns the base path for the app if the app is running behind a proxy
570
+ """
571
+ if environment == NotebookEnvironment.DATABRICKS:
572
+ context = _get_databricks_context()
573
+ return f"/driver-proxy/o/{context.org_id}/{context.cluster_id}/{port}/"
574
+ return ""
575
+
576
+
577
+ class DatabricksContext(NamedTuple):
578
+ host: str
579
+ org_id: str
580
+ cluster_id: str
581
+
582
+
583
+ def _get_databricks_context() -> DatabricksContext:
584
+ """
585
+ Returns the databricks context for constructing the base url
586
+ and the base_path for the app
558
587
  """
559
588
  import IPython
560
589
 
@@ -563,7 +592,16 @@ def _get_databricks_notebook_base_url() -> str:
563
592
  notebook_context = json.loads(
564
593
  dbutils.entry_point.getDbutils().notebook().getContext().toJson()
565
594
  )["tags"]
566
- host = notebook_context["browserHostName"]
567
- org_id = notebook_context["orgId"]
568
- cluster_id = notebook_context["clusterId"]
569
- return f"https://{host}/driver-proxy/o/{org_id}/{cluster_id}"
595
+
596
+ return DatabricksContext(
597
+ host=notebook_context["browserHostName"],
598
+ org_id=notebook_context["orgId"],
599
+ cluster_id=notebook_context["clusterId"],
600
+ )
601
+
602
+
603
+ def _get_databricks_notebook_base_url(context: DatabricksContext) -> str:
604
+ """
605
+ Returns base url of the databricks notebook by parsing the tags
606
+ """
607
+ return f"https://{context.host}/driver-proxy/o/{context.org_id}/{context.cluster_id}/"
phoenix/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.4.1rc4"
1
+ __version__ = "2.4.1rc6"