arize-phoenix 5.5.2__py3-none-any.whl → 5.7.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-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/METADATA +4 -7
- arize_phoenix-5.7.0.dist-info/RECORD +330 -0
- phoenix/config.py +50 -8
- phoenix/core/model.py +3 -3
- phoenix/core/model_schema.py +41 -50
- phoenix/core/model_schema_adapter.py +17 -16
- phoenix/datetime_utils.py +2 -2
- phoenix/db/bulk_inserter.py +10 -20
- phoenix/db/engines.py +2 -1
- phoenix/db/enums.py +2 -2
- phoenix/db/helpers.py +8 -7
- phoenix/db/insertion/dataset.py +9 -19
- phoenix/db/insertion/document_annotation.py +14 -13
- phoenix/db/insertion/helpers.py +6 -16
- phoenix/db/insertion/span_annotation.py +14 -13
- phoenix/db/insertion/trace_annotation.py +14 -13
- phoenix/db/insertion/types.py +19 -30
- phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py +8 -8
- phoenix/db/models.py +28 -28
- phoenix/experiments/evaluators/base.py +2 -1
- phoenix/experiments/evaluators/code_evaluators.py +4 -5
- phoenix/experiments/evaluators/llm_evaluators.py +157 -4
- phoenix/experiments/evaluators/utils.py +3 -2
- phoenix/experiments/functions.py +10 -21
- phoenix/experiments/tracing.py +2 -1
- phoenix/experiments/types.py +20 -29
- phoenix/experiments/utils.py +2 -1
- phoenix/inferences/errors.py +6 -5
- phoenix/inferences/fixtures.py +6 -5
- phoenix/inferences/inferences.py +37 -37
- phoenix/inferences/schema.py +11 -10
- phoenix/inferences/validation.py +13 -14
- phoenix/logging/_formatter.py +3 -3
- phoenix/metrics/__init__.py +5 -4
- phoenix/metrics/binning.py +2 -1
- phoenix/metrics/metrics.py +2 -1
- phoenix/metrics/mixins.py +7 -6
- phoenix/metrics/retrieval_metrics.py +2 -1
- phoenix/metrics/timeseries.py +5 -4
- phoenix/metrics/wrappers.py +2 -2
- phoenix/pointcloud/clustering.py +3 -4
- phoenix/pointcloud/pointcloud.py +7 -5
- phoenix/pointcloud/umap_parameters.py +2 -1
- phoenix/server/api/dataloaders/annotation_summaries.py +12 -19
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +2 -2
- phoenix/server/api/dataloaders/cache/two_tier_cache.py +3 -2
- phoenix/server/api/dataloaders/dataset_example_revisions.py +3 -8
- phoenix/server/api/dataloaders/dataset_example_spans.py +2 -5
- phoenix/server/api/dataloaders/document_evaluation_summaries.py +12 -18
- phoenix/server/api/dataloaders/document_evaluations.py +3 -7
- phoenix/server/api/dataloaders/document_retrieval_metrics.py +6 -13
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +4 -8
- phoenix/server/api/dataloaders/experiment_error_rates.py +2 -5
- phoenix/server/api/dataloaders/experiment_run_annotations.py +3 -7
- phoenix/server/api/dataloaders/experiment_run_counts.py +1 -5
- phoenix/server/api/dataloaders/experiment_sequence_number.py +2 -5
- phoenix/server/api/dataloaders/latency_ms_quantile.py +21 -30
- phoenix/server/api/dataloaders/min_start_or_max_end_times.py +7 -13
- phoenix/server/api/dataloaders/project_by_name.py +3 -3
- phoenix/server/api/dataloaders/record_counts.py +11 -18
- phoenix/server/api/dataloaders/span_annotations.py +3 -7
- phoenix/server/api/dataloaders/span_dataset_examples.py +3 -8
- phoenix/server/api/dataloaders/span_descendants.py +3 -7
- phoenix/server/api/dataloaders/span_projects.py +2 -2
- phoenix/server/api/dataloaders/token_counts.py +12 -19
- phoenix/server/api/dataloaders/trace_row_ids.py +3 -7
- phoenix/server/api/dataloaders/user_roles.py +3 -3
- phoenix/server/api/dataloaders/users.py +3 -3
- phoenix/server/api/helpers/__init__.py +4 -3
- phoenix/server/api/helpers/dataset_helpers.py +10 -9
- phoenix/server/api/helpers/playground_clients.py +671 -0
- phoenix/server/api/helpers/playground_registry.py +70 -0
- phoenix/server/api/helpers/playground_spans.py +325 -0
- phoenix/server/api/input_types/AddExamplesToDatasetInput.py +2 -2
- phoenix/server/api/input_types/AddSpansToDatasetInput.py +2 -2
- phoenix/server/api/input_types/ChatCompletionInput.py +38 -0
- phoenix/server/api/input_types/ChatCompletionMessageInput.py +13 -1
- phoenix/server/api/input_types/ClusterInput.py +2 -2
- phoenix/server/api/input_types/DeleteAnnotationsInput.py +1 -3
- phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +2 -2
- phoenix/server/api/input_types/DeleteExperimentsInput.py +1 -3
- phoenix/server/api/input_types/DimensionFilter.py +4 -4
- phoenix/server/api/input_types/GenerativeModelInput.py +17 -0
- phoenix/server/api/input_types/Granularity.py +1 -1
- phoenix/server/api/input_types/InvocationParameters.py +156 -13
- phoenix/server/api/input_types/PatchDatasetExamplesInput.py +2 -2
- phoenix/server/api/input_types/TemplateOptions.py +10 -0
- phoenix/server/api/mutations/__init__.py +4 -0
- phoenix/server/api/mutations/chat_mutations.py +374 -0
- phoenix/server/api/mutations/dataset_mutations.py +4 -4
- phoenix/server/api/mutations/experiment_mutations.py +1 -2
- phoenix/server/api/mutations/export_events_mutations.py +7 -7
- phoenix/server/api/mutations/span_annotations_mutations.py +4 -4
- phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
- phoenix/server/api/mutations/user_mutations.py +4 -4
- phoenix/server/api/openapi/schema.py +2 -2
- phoenix/server/api/queries.py +61 -72
- phoenix/server/api/routers/oauth2.py +4 -4
- phoenix/server/api/routers/v1/datasets.py +22 -36
- phoenix/server/api/routers/v1/evaluations.py +6 -5
- phoenix/server/api/routers/v1/experiment_evaluations.py +2 -2
- phoenix/server/api/routers/v1/experiment_runs.py +2 -2
- phoenix/server/api/routers/v1/experiments.py +4 -4
- phoenix/server/api/routers/v1/spans.py +13 -12
- phoenix/server/api/routers/v1/traces.py +5 -5
- phoenix/server/api/routers/v1/utils.py +5 -5
- phoenix/server/api/schema.py +42 -10
- phoenix/server/api/subscriptions.py +347 -494
- phoenix/server/api/types/AnnotationSummary.py +3 -3
- phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +44 -0
- phoenix/server/api/types/Cluster.py +8 -7
- phoenix/server/api/types/Dataset.py +5 -4
- phoenix/server/api/types/Dimension.py +3 -3
- phoenix/server/api/types/DocumentEvaluationSummary.py +8 -7
- phoenix/server/api/types/EmbeddingDimension.py +6 -5
- phoenix/server/api/types/EvaluationSummary.py +3 -3
- phoenix/server/api/types/Event.py +7 -7
- phoenix/server/api/types/Experiment.py +3 -3
- phoenix/server/api/types/ExperimentComparison.py +2 -4
- phoenix/server/api/types/GenerativeProvider.py +27 -3
- phoenix/server/api/types/Inferences.py +9 -8
- phoenix/server/api/types/InferencesRole.py +2 -2
- phoenix/server/api/types/Model.py +2 -2
- phoenix/server/api/types/Project.py +11 -18
- phoenix/server/api/types/Segments.py +3 -3
- phoenix/server/api/types/Span.py +45 -7
- phoenix/server/api/types/TemplateLanguage.py +9 -0
- phoenix/server/api/types/TimeSeries.py +8 -7
- phoenix/server/api/types/Trace.py +2 -2
- phoenix/server/api/types/UMAPPoints.py +6 -6
- phoenix/server/api/types/User.py +3 -3
- phoenix/server/api/types/node.py +1 -3
- phoenix/server/api/types/pagination.py +4 -4
- phoenix/server/api/utils.py +2 -4
- phoenix/server/app.py +76 -37
- phoenix/server/bearer_auth.py +4 -10
- phoenix/server/dml_event.py +3 -3
- phoenix/server/dml_event_handler.py +10 -24
- phoenix/server/grpc_server.py +3 -2
- phoenix/server/jwt_store.py +22 -21
- phoenix/server/main.py +17 -4
- phoenix/server/oauth2.py +3 -2
- phoenix/server/rate_limiters.py +5 -8
- phoenix/server/static/.vite/manifest.json +31 -31
- phoenix/server/static/assets/components-Csu8UKOs.js +1612 -0
- phoenix/server/static/assets/{index-DCzakdJq.js → index-Bk5C9EA7.js} +2 -2
- phoenix/server/static/assets/{pages-CAL1FDMt.js → pages-UeWaKXNs.js} +337 -442
- phoenix/server/static/assets/{vendor-6IcPAw_j.js → vendor-CtqfhlbC.js} +6 -6
- phoenix/server/static/assets/{vendor-arizeai-DRZuoyuF.js → vendor-arizeai-C_3SBz56.js} +2 -2
- phoenix/server/static/assets/{vendor-codemirror-DVE2_WBr.js → vendor-codemirror-wfdk9cjp.js} +1 -1
- phoenix/server/static/assets/{vendor-recharts-DwrexFA4.js → vendor-recharts-BiVnSv90.js} +1 -1
- phoenix/server/templates/index.html +1 -0
- phoenix/server/thread_server.py +1 -1
- phoenix/server/types.py +17 -29
- phoenix/services.py +8 -3
- phoenix/session/client.py +12 -24
- phoenix/session/data_extractor.py +3 -3
- phoenix/session/evaluation.py +1 -2
- phoenix/session/session.py +26 -21
- phoenix/trace/attributes.py +16 -28
- phoenix/trace/dsl/filter.py +17 -21
- phoenix/trace/dsl/helpers.py +3 -3
- phoenix/trace/dsl/query.py +13 -22
- phoenix/trace/fixtures.py +11 -17
- phoenix/trace/otel.py +5 -15
- phoenix/trace/projects.py +3 -2
- phoenix/trace/schemas.py +2 -2
- phoenix/trace/span_evaluations.py +9 -8
- phoenix/trace/span_json_decoder.py +3 -3
- phoenix/trace/span_json_encoder.py +2 -2
- phoenix/trace/trace_dataset.py +6 -5
- phoenix/trace/utils.py +6 -6
- phoenix/utilities/deprecation.py +3 -2
- phoenix/utilities/error_handling.py +3 -2
- phoenix/utilities/json.py +2 -1
- phoenix/utilities/logging.py +2 -2
- phoenix/utilities/project.py +1 -1
- phoenix/utilities/re.py +3 -4
- phoenix/utilities/template_formatters.py +16 -5
- phoenix/version.py +1 -1
- arize_phoenix-5.5.2.dist-info/RECORD +0 -321
- phoenix/server/static/assets/components-hX0LgYz3.js +0 -1428
- {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,24 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from asyncio import gather
|
|
5
|
+
from collections.abc import Callable, Iterable, Iterator, Mapping
|
|
5
6
|
from inspect import getmro
|
|
6
7
|
from itertools import chain
|
|
7
|
-
from typing import
|
|
8
|
-
Any,
|
|
9
|
-
Callable,
|
|
10
|
-
Generic,
|
|
11
|
-
Iterable,
|
|
12
|
-
Iterator,
|
|
13
|
-
Mapping,
|
|
14
|
-
Optional,
|
|
15
|
-
Set,
|
|
16
|
-
Tuple,
|
|
17
|
-
Type,
|
|
18
|
-
TypedDict,
|
|
19
|
-
TypeVar,
|
|
20
|
-
Union,
|
|
21
|
-
cast,
|
|
22
|
-
)
|
|
8
|
+
from typing import Any, Generic, Optional, TypedDict, TypeVar, Union, cast
|
|
23
9
|
|
|
24
10
|
from sqlalchemy import Select, select
|
|
25
11
|
from typing_extensions import TypeAlias, Unpack
|
|
@@ -54,7 +40,7 @@ _DmlEventT = TypeVar("_DmlEventT", bound=DmlEvent)
|
|
|
54
40
|
class _DmlEventQueue(Generic[_DmlEventT]):
|
|
55
41
|
def __init__(self, **kwargs: Any) -> None:
|
|
56
42
|
super().__init__(**kwargs)
|
|
57
|
-
self._events:
|
|
43
|
+
self._events: set[_DmlEventT] = set()
|
|
58
44
|
|
|
59
45
|
@property
|
|
60
46
|
def empty(self) -> bool:
|
|
@@ -120,7 +106,7 @@ class _GenericDmlEventHandler(_DmlEventHandler[DmlEvent]):
|
|
|
120
106
|
for id_ in e.ids:
|
|
121
107
|
self._update(e.table, id_)
|
|
122
108
|
|
|
123
|
-
def _update(self, table:
|
|
109
|
+
def _update(self, table: type[Base], id_: int) -> None:
|
|
124
110
|
self._last_updated_at.set(table, id_)
|
|
125
111
|
|
|
126
112
|
|
|
@@ -146,9 +132,9 @@ class _SpanDeleteEventHandler(_SpanDmlEventHandler):
|
|
|
146
132
|
|
|
147
133
|
|
|
148
134
|
_AnnotationTable: TypeAlias = Union[
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
135
|
+
type[SpanAnnotation],
|
|
136
|
+
type[TraceAnnotation],
|
|
137
|
+
type[DocumentAnnotation],
|
|
152
138
|
]
|
|
153
139
|
|
|
154
140
|
_AnnotationDmlEventT = TypeVar(
|
|
@@ -165,7 +151,7 @@ class _AnnotationDmlEventHandler(
|
|
|
165
151
|
ABC,
|
|
166
152
|
):
|
|
167
153
|
_table: _AnnotationTable
|
|
168
|
-
_base_stmt: Union[Select[
|
|
154
|
+
_base_stmt: Union[Select[tuple[int, str]], Select[tuple[int]]] = (
|
|
169
155
|
select(Project.id).join_from(Project, Trace).distinct()
|
|
170
156
|
)
|
|
171
157
|
|
|
@@ -175,7 +161,7 @@ class _AnnotationDmlEventHandler(
|
|
|
175
161
|
if self._cache_for_dataloaders:
|
|
176
162
|
self._stmt = self._stmt.add_columns(self._table.name)
|
|
177
163
|
|
|
178
|
-
def _get_stmt(self) -> Union[Select[
|
|
164
|
+
def _get_stmt(self) -> Union[Select[tuple[int, str]], Select[tuple[int]]]:
|
|
179
165
|
ids = set(chain.from_iterable(e.ids for e in self._batch))
|
|
180
166
|
return self._stmt.where(self._table.id.in_(ids))
|
|
181
167
|
|
|
@@ -242,7 +228,7 @@ class DmlEventHandler:
|
|
|
242
228
|
cache_for_dataloaders=cache_for_dataloaders,
|
|
243
229
|
sleep_seconds=sleep_seconds,
|
|
244
230
|
)
|
|
245
|
-
self._handlers: Mapping[
|
|
231
|
+
self._handlers: Mapping[type[DmlEvent], Iterable[_DmlEventHandler[Any]]] = {
|
|
246
232
|
DmlEvent: [_GenericDmlEventHandler(**kwargs)],
|
|
247
233
|
SpanDmlEvent: [_SpanDmlEventHandler(**kwargs)],
|
|
248
234
|
SpanDeleteEvent: [_SpanDeleteEventHandler(**kwargs)],
|
phoenix/server/grpc_server.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
2
3
|
|
|
3
4
|
import grpc
|
|
4
5
|
from grpc.aio import RpcContext, Server, ServerInterceptor
|
|
@@ -66,7 +67,7 @@ class GrpcServer:
|
|
|
66
67
|
async def __aenter__(self) -> None:
|
|
67
68
|
if self._disabled:
|
|
68
69
|
return
|
|
69
|
-
interceptors:
|
|
70
|
+
interceptors: list[ServerInterceptor] = []
|
|
70
71
|
if self._token_store:
|
|
71
72
|
interceptors.append(ApiKeyInterceptor(self._token_store))
|
|
72
73
|
if self._enable_prometheus:
|
phoenix/server/jwt_store.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from asyncio import create_task, gather, sleep
|
|
4
|
+
from collections.abc import Callable, Coroutine
|
|
4
5
|
from copy import deepcopy
|
|
5
6
|
from dataclasses import replace
|
|
6
7
|
from datetime import datetime, timezone
|
|
7
8
|
from functools import cached_property, singledispatchmethod
|
|
8
|
-
from typing import Any,
|
|
9
|
+
from typing import Any, Generic, Optional, TypeVar
|
|
9
10
|
|
|
10
11
|
from authlib.jose import jwt
|
|
11
12
|
from authlib.jose.errors import JoseError
|
|
@@ -65,7 +66,7 @@ class JwtStore:
|
|
|
65
66
|
self._api_key_store = _ApiKeyStore(*args, **kwargs)
|
|
66
67
|
|
|
67
68
|
@cached_property
|
|
68
|
-
def _stores(self) ->
|
|
69
|
+
def _stores(self) -> tuple[DaemonTask, ...]:
|
|
69
70
|
return tuple(dt for dt in self.__dict__.values() if isinstance(dt, _Store))
|
|
70
71
|
|
|
71
72
|
async def __aenter__(self) -> None:
|
|
@@ -131,34 +132,34 @@ class JwtStore:
|
|
|
131
132
|
async def create_password_reset_token(
|
|
132
133
|
self,
|
|
133
134
|
claim: PasswordResetTokenClaims,
|
|
134
|
-
) ->
|
|
135
|
+
) -> tuple[PasswordResetToken, PasswordResetTokenId]:
|
|
135
136
|
return await self._password_reset_token_store.create(claim)
|
|
136
137
|
|
|
137
138
|
async def create_access_token(
|
|
138
139
|
self,
|
|
139
140
|
claim: AccessTokenClaims,
|
|
140
|
-
) ->
|
|
141
|
+
) -> tuple[AccessToken, AccessTokenId]:
|
|
141
142
|
return await self._access_token_store.create(claim)
|
|
142
143
|
|
|
143
144
|
async def create_refresh_token(
|
|
144
145
|
self,
|
|
145
146
|
claim: RefreshTokenClaims,
|
|
146
|
-
) ->
|
|
147
|
+
) -> tuple[RefreshToken, RefreshTokenId]:
|
|
147
148
|
return await self._refresh_token_store.create(claim)
|
|
148
149
|
|
|
149
150
|
async def create_api_key(
|
|
150
151
|
self,
|
|
151
152
|
claim: ApiKeyClaims,
|
|
152
|
-
) ->
|
|
153
|
+
) -> tuple[ApiKey, ApiKeyId]:
|
|
153
154
|
return await self._api_key_store.create(claim)
|
|
154
155
|
|
|
155
156
|
async def revoke(self, *token_ids: TokenId) -> None:
|
|
156
157
|
if not token_ids:
|
|
157
158
|
return
|
|
158
|
-
password_reset_token_ids:
|
|
159
|
-
access_token_ids:
|
|
160
|
-
refresh_token_ids:
|
|
161
|
-
api_key_ids:
|
|
159
|
+
password_reset_token_ids: list[PasswordResetTokenId] = []
|
|
160
|
+
access_token_ids: list[AccessTokenId] = []
|
|
161
|
+
refresh_token_ids: list[RefreshTokenId] = []
|
|
162
|
+
api_key_ids: list[ApiKeyId] = []
|
|
162
163
|
for token_id in token_ids:
|
|
163
164
|
if isinstance(token_id, PasswordResetTokenId):
|
|
164
165
|
password_reset_token_ids.append(token_id)
|
|
@@ -168,7 +169,7 @@ class JwtStore:
|
|
|
168
169
|
refresh_token_ids.append(token_id)
|
|
169
170
|
elif isinstance(token_id, ApiKeyId):
|
|
170
171
|
api_key_ids.append(token_id)
|
|
171
|
-
coroutines:
|
|
172
|
+
coroutines: list[Coroutine[None, None, None]] = []
|
|
172
173
|
if password_reset_token_ids:
|
|
173
174
|
coroutines.append(self._password_reset_token_store.revoke(*password_reset_token_ids))
|
|
174
175
|
if access_token_ids:
|
|
@@ -202,7 +203,7 @@ _RecordT = TypeVar(
|
|
|
202
203
|
|
|
203
204
|
class _Claims(Generic[_TokenIdT, _ClaimSetT]):
|
|
204
205
|
def __init__(self) -> None:
|
|
205
|
-
self._cache:
|
|
206
|
+
self._cache: dict[_TokenIdT, _ClaimSetT] = {}
|
|
206
207
|
|
|
207
208
|
def __getitem__(self, token_id: _TokenIdT) -> Optional[_ClaimSetT]:
|
|
208
209
|
claim = self._cache.get(token_id)
|
|
@@ -223,7 +224,7 @@ class _Claims(Generic[_TokenIdT, _ClaimSetT]):
|
|
|
223
224
|
|
|
224
225
|
|
|
225
226
|
class _Store(DaemonTask, Generic[_ClaimSetT, _TokenT, _TokenIdT, _RecordT], ABC):
|
|
226
|
-
_table:
|
|
227
|
+
_table: type[_RecordT]
|
|
227
228
|
_token_id: Callable[[int], _TokenIdT]
|
|
228
229
|
_token: Callable[[str], _TokenT]
|
|
229
230
|
|
|
@@ -244,7 +245,7 @@ class _Store(DaemonTask, Generic[_ClaimSetT, _TokenT, _TokenIdT, _RecordT], ABC)
|
|
|
244
245
|
self._algorithm = algorithm
|
|
245
246
|
|
|
246
247
|
def _encode(self, claim: ClaimSet) -> str:
|
|
247
|
-
payload:
|
|
248
|
+
payload: dict[str, Any] = dict(jti=claim.token_id)
|
|
248
249
|
header = {"alg": self._algorithm}
|
|
249
250
|
jwt_bytes: bytes = jwt.encode(header=header, payload=payload, key=self._secret)
|
|
250
251
|
return jwt_bytes.decode()
|
|
@@ -275,12 +276,12 @@ class _Store(DaemonTask, Generic[_ClaimSetT, _TokenT, _TokenIdT, _RecordT], ABC)
|
|
|
275
276
|
await session.execute(stmt)
|
|
276
277
|
|
|
277
278
|
@abstractmethod
|
|
278
|
-
def _from_db(self, record: _RecordT, role: UserRole) ->
|
|
279
|
+
def _from_db(self, record: _RecordT, role: UserRole) -> tuple[_TokenIdT, _ClaimSetT]: ...
|
|
279
280
|
|
|
280
281
|
@abstractmethod
|
|
281
282
|
def _to_db(self, claims: _ClaimSetT) -> _RecordT: ...
|
|
282
283
|
|
|
283
|
-
async def create(self, claim: _ClaimSetT) ->
|
|
284
|
+
async def create(self, claim: _ClaimSetT) -> tuple[_TokenT, _TokenIdT]:
|
|
284
285
|
record = self._to_db(claim)
|
|
285
286
|
async with self._db() as session:
|
|
286
287
|
session.add(record)
|
|
@@ -303,7 +304,7 @@ class _Store(DaemonTask, Generic[_ClaimSetT, _TokenT, _TokenIdT, _RecordT], ABC)
|
|
|
303
304
|
self._claims = claims
|
|
304
305
|
|
|
305
306
|
@cached_property
|
|
306
|
-
def _update_stmt(self) -> Select[
|
|
307
|
+
def _update_stmt(self) -> Select[tuple[_RecordT, str]]:
|
|
307
308
|
return (
|
|
308
309
|
select(self._table, models.UserRole.name)
|
|
309
310
|
.join_from(self._table, models.User)
|
|
@@ -340,7 +341,7 @@ class _PasswordResetTokenStore(
|
|
|
340
341
|
self,
|
|
341
342
|
record: models.PasswordResetToken,
|
|
342
343
|
user_role: UserRole,
|
|
343
|
-
) ->
|
|
344
|
+
) -> tuple[PasswordResetTokenId, PasswordResetTokenClaims]:
|
|
344
345
|
token_id = PasswordResetTokenId(record.id)
|
|
345
346
|
return token_id, PasswordResetTokenClaims(
|
|
346
347
|
token_id=token_id,
|
|
@@ -379,7 +380,7 @@ class _AccessTokenStore(
|
|
|
379
380
|
self,
|
|
380
381
|
record: models.AccessToken,
|
|
381
382
|
user_role: UserRole,
|
|
382
|
-
) ->
|
|
383
|
+
) -> tuple[AccessTokenId, AccessTokenClaims]:
|
|
383
384
|
token_id = AccessTokenId(record.id)
|
|
384
385
|
refresh_token_id = RefreshTokenId(record.refresh_token_id)
|
|
385
386
|
return token_id, AccessTokenClaims(
|
|
@@ -423,7 +424,7 @@ class _RefreshTokenStore(
|
|
|
423
424
|
self,
|
|
424
425
|
record: models.RefreshToken,
|
|
425
426
|
user_role: UserRole,
|
|
426
|
-
) ->
|
|
427
|
+
) -> tuple[RefreshTokenId, RefreshTokenClaims]:
|
|
427
428
|
token_id = RefreshTokenId(record.id)
|
|
428
429
|
return token_id, RefreshTokenClaims(
|
|
429
430
|
token_id=token_id,
|
|
@@ -469,7 +470,7 @@ class _ApiKeyStore(
|
|
|
469
470
|
self,
|
|
470
471
|
record: models.ApiKey,
|
|
471
472
|
user_role: UserRole,
|
|
472
|
-
) ->
|
|
473
|
+
) -> tuple[ApiKeyId, ApiKeyClaims]:
|
|
473
474
|
token_id = ApiKeyId(record.id)
|
|
474
475
|
return token_id, ApiKeyClaims(
|
|
475
476
|
token_id=token_id,
|
phoenix/server/main.py
CHANGED
|
@@ -6,7 +6,7 @@ from argparse import SUPPRESS, ArgumentParser
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from threading import Thread
|
|
8
8
|
from time import sleep, time
|
|
9
|
-
from typing import
|
|
9
|
+
from typing import Optional
|
|
10
10
|
from urllib.parse import urljoin
|
|
11
11
|
|
|
12
12
|
from jinja2 import BaseLoader, Environment
|
|
@@ -21,6 +21,7 @@ from phoenix.config import (
|
|
|
21
21
|
get_env_database_schema,
|
|
22
22
|
get_env_db_logging_level,
|
|
23
23
|
get_env_enable_prometheus,
|
|
24
|
+
get_env_enable_websockets,
|
|
24
25
|
get_env_grpc_port,
|
|
25
26
|
get_env_host,
|
|
26
27
|
get_env_host_root_path,
|
|
@@ -95,6 +96,7 @@ _WELCOME_MESSAGE = Environment(loader=BaseLoader()).from_string("""
|
|
|
95
96
|
| 🚀 Phoenix Server 🚀
|
|
96
97
|
| Phoenix UI: {{ ui_path }}
|
|
97
98
|
| Authentication: {{ auth_enabled }}
|
|
99
|
+
| Websockets: {{ websockets_enabled }}
|
|
98
100
|
| Log traces:
|
|
99
101
|
| - gRPC: {{ grpc_path }}
|
|
100
102
|
| - HTTP: {{ http_path }}
|
|
@@ -162,7 +164,7 @@ def main() -> None:
|
|
|
162
164
|
parser.add_argument("--debug", action="store_true", help=SUPPRESS)
|
|
163
165
|
parser.add_argument("--dev", action="store_true", help=SUPPRESS)
|
|
164
166
|
parser.add_argument("--no-ui", action="store_true", help=SUPPRESS)
|
|
165
|
-
|
|
167
|
+
parser.add_argument("--enable-websockets", type=str, help=SUPPRESS)
|
|
166
168
|
subparsers = parser.add_subparsers(dest="command", required=True, help=SUPPRESS)
|
|
167
169
|
|
|
168
170
|
serve_parser = subparsers.add_parser("serve")
|
|
@@ -312,8 +314,8 @@ def main() -> None:
|
|
|
312
314
|
|
|
313
315
|
authentication_enabled, secret = get_env_auth_settings()
|
|
314
316
|
|
|
315
|
-
fixture_spans:
|
|
316
|
-
fixture_evals:
|
|
317
|
+
fixture_spans: list[Span] = []
|
|
318
|
+
fixture_evals: list[pb.Evaluation] = []
|
|
317
319
|
if trace_dataset_name is not None:
|
|
318
320
|
fixture_spans, fixture_evals = reset_fixture_span_ids_and_timestamps(
|
|
319
321
|
(
|
|
@@ -348,6 +350,14 @@ def main() -> None:
|
|
|
348
350
|
corpus_model = (
|
|
349
351
|
None if corpus_inferences is None else create_model_from_inferences(corpus_inferences)
|
|
350
352
|
)
|
|
353
|
+
|
|
354
|
+
# Get enable_websockets from environment variable or command line argument
|
|
355
|
+
enable_websockets = get_env_enable_websockets()
|
|
356
|
+
if args.enable_websockets is not None:
|
|
357
|
+
enable_websockets = args.enable_websockets.lower() == "true"
|
|
358
|
+
if enable_websockets is None:
|
|
359
|
+
enable_websockets = True
|
|
360
|
+
|
|
351
361
|
# Print information about the server
|
|
352
362
|
root_path = urljoin(f"http://{host}:{port}", host_root_path)
|
|
353
363
|
msg = _WELCOME_MESSAGE.render(
|
|
@@ -358,6 +368,7 @@ def main() -> None:
|
|
|
358
368
|
storage=get_printable_db_url(db_connection_str),
|
|
359
369
|
schema=get_env_database_schema(),
|
|
360
370
|
auth_enabled=authentication_enabled,
|
|
371
|
+
websockets_enabled=enable_websockets,
|
|
361
372
|
)
|
|
362
373
|
if sys.platform.startswith("win"):
|
|
363
374
|
msg = codecs.encode(msg, "ascii", errors="ignore").decode("ascii").strip()
|
|
@@ -382,10 +393,12 @@ def main() -> None:
|
|
|
382
393
|
connection_method="STARTTLS",
|
|
383
394
|
validate_certs=get_env_smtp_validate_certs(),
|
|
384
395
|
)
|
|
396
|
+
|
|
385
397
|
app = create_app(
|
|
386
398
|
db=factory,
|
|
387
399
|
export_path=export_path,
|
|
388
400
|
model=model,
|
|
401
|
+
enable_websockets=enable_websockets,
|
|
389
402
|
authentication_enabled=authentication_enabled,
|
|
390
403
|
umap_params=umap_params,
|
|
391
404
|
corpus=corpus_model,
|
phoenix/server/oauth2.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
4
|
from authlib.integrations.base_client import BaseApp
|
|
4
5
|
from authlib.integrations.base_client.async_app import AsyncOAuth2Mixin
|
|
@@ -24,7 +25,7 @@ class OAuth2Client(AsyncOAuth2Mixin, AsyncOpenIDMixin, BaseApp): # type:ignore[
|
|
|
24
25
|
|
|
25
26
|
class OAuth2Clients:
|
|
26
27
|
def __init__(self) -> None:
|
|
27
|
-
self._clients:
|
|
28
|
+
self._clients: dict[str, OAuth2Client] = {}
|
|
28
29
|
|
|
29
30
|
def add_client(self, config: OAuth2ClientConfig) -> None:
|
|
30
31
|
if (idp_name := config.idp_name) in self._clients:
|
phoenix/server/rate_limiters.py
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import time
|
|
3
3
|
from collections import defaultdict
|
|
4
|
+
from collections.abc import Callable, Coroutine
|
|
4
5
|
from functools import partial
|
|
5
6
|
from typing import (
|
|
6
7
|
Any,
|
|
7
|
-
Callable,
|
|
8
|
-
Coroutine,
|
|
9
|
-
DefaultDict,
|
|
10
|
-
List,
|
|
11
8
|
Optional,
|
|
12
9
|
Pattern, # import from re module when we drop support for 3.8
|
|
13
10
|
Union,
|
|
@@ -98,7 +95,7 @@ class ServerRateLimiter:
|
|
|
98
95
|
self._last_cleanup_time = time.time()
|
|
99
96
|
|
|
100
97
|
def _reset_rate_limiters(self) -> None:
|
|
101
|
-
self.cache_partitions:
|
|
98
|
+
self.cache_partitions: list[defaultdict[Any, TokenBucket]] = [
|
|
102
99
|
defaultdict(self.bucket_factory) for _ in range(self.num_partitions)
|
|
103
100
|
]
|
|
104
101
|
|
|
@@ -107,10 +104,10 @@ class ServerRateLimiter:
|
|
|
107
104
|
int(timestamp // self.partition_seconds) % self.num_partitions
|
|
108
105
|
) # a cyclic bucket index
|
|
109
106
|
|
|
110
|
-
def _active_partition_indices(self, current_index: int) ->
|
|
107
|
+
def _active_partition_indices(self, current_index: int) -> list[int]:
|
|
111
108
|
return [(current_index - ii) % self.num_partitions for ii in range(self.active_partitions)]
|
|
112
109
|
|
|
113
|
-
def _inactive_partition_indices(self, current_index: int) ->
|
|
110
|
+
def _inactive_partition_indices(self, current_index: int) -> list[int]:
|
|
114
111
|
active_indices = set(self._active_partition_indices(current_index))
|
|
115
112
|
all_indices = set(range(self.num_partitions))
|
|
116
113
|
return list(all_indices - active_indices)
|
|
@@ -156,7 +153,7 @@ class ServerRateLimiter:
|
|
|
156
153
|
|
|
157
154
|
|
|
158
155
|
def fastapi_ip_rate_limiter(
|
|
159
|
-
rate_limiter: ServerRateLimiter, paths: Optional[
|
|
156
|
+
rate_limiter: ServerRateLimiter, paths: Optional[list[Union[str, Pattern[str]]]] = None
|
|
160
157
|
) -> Callable[[Request], Coroutine[Any, Any, Request]]:
|
|
161
158
|
async def dependency(request: Request) -> Request:
|
|
162
159
|
if paths is None or any(path_match(request.url.path, path) for path in paths):
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_components-
|
|
3
|
-
"file": "assets/components-
|
|
2
|
+
"_components-Csu8UKOs.js": {
|
|
3
|
+
"file": "assets/components-Csu8UKOs.js",
|
|
4
4
|
"name": "components",
|
|
5
5
|
"imports": [
|
|
6
|
-
"_vendor-
|
|
7
|
-
"_vendor-arizeai-
|
|
8
|
-
"_pages-
|
|
6
|
+
"_vendor-CtqfhlbC.js",
|
|
7
|
+
"_vendor-arizeai-C_3SBz56.js",
|
|
8
|
+
"_pages-UeWaKXNs.js",
|
|
9
9
|
"_vendor-three-DwGkEfCM.js",
|
|
10
|
-
"_vendor-codemirror-
|
|
10
|
+
"_vendor-codemirror-wfdk9cjp.js"
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
|
-
"_pages-
|
|
14
|
-
"file": "assets/pages-
|
|
13
|
+
"_pages-UeWaKXNs.js": {
|
|
14
|
+
"file": "assets/pages-UeWaKXNs.js",
|
|
15
15
|
"name": "pages",
|
|
16
16
|
"imports": [
|
|
17
|
-
"_vendor-
|
|
18
|
-
"_vendor-arizeai-
|
|
19
|
-
"_components-
|
|
20
|
-
"_vendor-recharts-
|
|
21
|
-
"_vendor-codemirror-
|
|
17
|
+
"_vendor-CtqfhlbC.js",
|
|
18
|
+
"_vendor-arizeai-C_3SBz56.js",
|
|
19
|
+
"_components-Csu8UKOs.js",
|
|
20
|
+
"_vendor-recharts-BiVnSv90.js",
|
|
21
|
+
"_vendor-codemirror-wfdk9cjp.js"
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
24
|
"_vendor-!~{003}~.js": {
|
|
25
25
|
"file": "assets/vendor-DxkFTwjz.css",
|
|
26
26
|
"src": "_vendor-!~{003}~.js"
|
|
27
27
|
},
|
|
28
|
-
"_vendor-
|
|
29
|
-
"file": "assets/vendor-
|
|
28
|
+
"_vendor-CtqfhlbC.js": {
|
|
29
|
+
"file": "assets/vendor-CtqfhlbC.js",
|
|
30
30
|
"name": "vendor",
|
|
31
31
|
"imports": [
|
|
32
32
|
"_vendor-three-DwGkEfCM.js"
|
|
@@ -35,25 +35,25 @@
|
|
|
35
35
|
"assets/vendor-DxkFTwjz.css"
|
|
36
36
|
]
|
|
37
37
|
},
|
|
38
|
-
"_vendor-arizeai-
|
|
39
|
-
"file": "assets/vendor-arizeai-
|
|
38
|
+
"_vendor-arizeai-C_3SBz56.js": {
|
|
39
|
+
"file": "assets/vendor-arizeai-C_3SBz56.js",
|
|
40
40
|
"name": "vendor-arizeai",
|
|
41
41
|
"imports": [
|
|
42
|
-
"_vendor-
|
|
42
|
+
"_vendor-CtqfhlbC.js"
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
|
-
"_vendor-codemirror-
|
|
46
|
-
"file": "assets/vendor-codemirror-
|
|
45
|
+
"_vendor-codemirror-wfdk9cjp.js": {
|
|
46
|
+
"file": "assets/vendor-codemirror-wfdk9cjp.js",
|
|
47
47
|
"name": "vendor-codemirror",
|
|
48
48
|
"imports": [
|
|
49
|
-
"_vendor-
|
|
49
|
+
"_vendor-CtqfhlbC.js"
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
|
-
"_vendor-recharts-
|
|
53
|
-
"file": "assets/vendor-recharts-
|
|
52
|
+
"_vendor-recharts-BiVnSv90.js": {
|
|
53
|
+
"file": "assets/vendor-recharts-BiVnSv90.js",
|
|
54
54
|
"name": "vendor-recharts",
|
|
55
55
|
"imports": [
|
|
56
|
-
"_vendor-
|
|
56
|
+
"_vendor-CtqfhlbC.js"
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
59
|
"_vendor-three-DwGkEfCM.js": {
|
|
@@ -61,18 +61,18 @@
|
|
|
61
61
|
"name": "vendor-three"
|
|
62
62
|
},
|
|
63
63
|
"index.tsx": {
|
|
64
|
-
"file": "assets/index-
|
|
64
|
+
"file": "assets/index-Bk5C9EA7.js",
|
|
65
65
|
"name": "index",
|
|
66
66
|
"src": "index.tsx",
|
|
67
67
|
"isEntry": true,
|
|
68
68
|
"imports": [
|
|
69
|
-
"_vendor-
|
|
70
|
-
"_vendor-arizeai-
|
|
71
|
-
"_pages-
|
|
72
|
-
"_components-
|
|
69
|
+
"_vendor-CtqfhlbC.js",
|
|
70
|
+
"_vendor-arizeai-C_3SBz56.js",
|
|
71
|
+
"_pages-UeWaKXNs.js",
|
|
72
|
+
"_components-Csu8UKOs.js",
|
|
73
73
|
"_vendor-three-DwGkEfCM.js",
|
|
74
|
-
"_vendor-recharts-
|
|
75
|
-
"_vendor-codemirror-
|
|
74
|
+
"_vendor-recharts-BiVnSv90.js",
|
|
75
|
+
"_vendor-codemirror-wfdk9cjp.js"
|
|
76
76
|
]
|
|
77
77
|
}
|
|
78
78
|
}
|