arize-phoenix 5.6.0__py3-none-any.whl → 5.8.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.6.0.dist-info → arize_phoenix-5.8.0.dist-info}/METADATA +4 -6
- {arize_phoenix-5.6.0.dist-info → arize_phoenix-5.8.0.dist-info}/RECORD +39 -30
- {arize_phoenix-5.6.0.dist-info → arize_phoenix-5.8.0.dist-info}/WHEEL +1 -1
- phoenix/config.py +58 -0
- phoenix/server/api/helpers/playground_clients.py +758 -0
- phoenix/server/api/helpers/playground_registry.py +70 -0
- phoenix/server/api/helpers/playground_spans.py +422 -0
- phoenix/server/api/input_types/ChatCompletionInput.py +38 -0
- phoenix/server/api/input_types/GenerativeModelInput.py +17 -0
- phoenix/server/api/input_types/InvocationParameters.py +155 -13
- phoenix/server/api/input_types/TemplateOptions.py +10 -0
- phoenix/server/api/mutations/__init__.py +4 -0
- phoenix/server/api/mutations/chat_mutations.py +355 -0
- phoenix/server/api/queries.py +41 -52
- phoenix/server/api/schema.py +42 -10
- phoenix/server/api/subscriptions.py +378 -595
- phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +46 -0
- phoenix/server/api/types/GenerativeProvider.py +27 -3
- phoenix/server/api/types/Span.py +37 -0
- phoenix/server/api/types/TemplateLanguage.py +9 -0
- phoenix/server/app.py +75 -13
- phoenix/server/grpc_server.py +3 -1
- phoenix/server/main.py +14 -1
- phoenix/server/static/.vite/manifest.json +31 -31
- phoenix/server/static/assets/{components-C70HJiXz.js → components-MllbfxfJ.js} +168 -150
- phoenix/server/static/assets/{index-DLe1Oo3l.js → index-BVO2YcT1.js} +2 -2
- phoenix/server/static/assets/{pages-C8-Sl7JI.js → pages-BHfC6jnL.js} +464 -310
- phoenix/server/static/assets/{vendor-CtqfhlbC.js → vendor-BEuNhfwH.js} +1 -1
- phoenix/server/static/assets/{vendor-arizeai-C_3SBz56.js → vendor-arizeai-Bskhzyjm.js} +1 -1
- phoenix/server/static/assets/{vendor-codemirror-wfdk9cjp.js → vendor-codemirror-DLlXCf0x.js} +1 -1
- phoenix/server/static/assets/{vendor-recharts-BiVnSv90.js → vendor-recharts-CRqhvLYg.js} +1 -1
- phoenix/server/templates/index.html +1 -0
- phoenix/services.py +4 -0
- phoenix/session/session.py +15 -1
- phoenix/utilities/template_formatters.py +11 -1
- phoenix/version.py +1 -1
- {arize_phoenix-5.6.0.dist-info → arize_phoenix-5.8.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-5.6.0.dist-info → arize_phoenix-5.8.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-5.6.0.dist-info → arize_phoenix-5.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import strawberry
|
|
4
|
+
from strawberry.relay import GlobalID
|
|
5
|
+
|
|
6
|
+
from .Experiment import Experiment
|
|
7
|
+
from .ExperimentRun import ExperimentRun
|
|
8
|
+
from .Span import Span
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@strawberry.interface
|
|
12
|
+
class ChatCompletionSubscriptionPayload:
|
|
13
|
+
dataset_example_id: Optional[GlobalID] = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@strawberry.type
|
|
17
|
+
class TextChunk(ChatCompletionSubscriptionPayload):
|
|
18
|
+
content: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@strawberry.type
|
|
22
|
+
class FunctionCallChunk(ChatCompletionSubscriptionPayload):
|
|
23
|
+
name: str
|
|
24
|
+
arguments: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@strawberry.type
|
|
28
|
+
class ToolCallChunk(ChatCompletionSubscriptionPayload):
|
|
29
|
+
id: str
|
|
30
|
+
function: FunctionCallChunk
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@strawberry.type
|
|
34
|
+
class ChatCompletionSubscriptionResult(ChatCompletionSubscriptionPayload):
|
|
35
|
+
span: Optional[Span] = None
|
|
36
|
+
experiment_run: Optional[ExperimentRun] = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@strawberry.type
|
|
40
|
+
class ChatCompletionSubscriptionError(ChatCompletionSubscriptionPayload):
|
|
41
|
+
message: str
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@strawberry.type
|
|
45
|
+
class ChatCompletionSubscriptionExperiment(ChatCompletionSubscriptionPayload):
|
|
46
|
+
experiment: Experiment
|
|
@@ -5,12 +5,36 @@ import strawberry
|
|
|
5
5
|
|
|
6
6
|
@strawberry.enum
|
|
7
7
|
class GenerativeProviderKey(Enum):
|
|
8
|
-
OPENAI = "
|
|
9
|
-
ANTHROPIC = "
|
|
10
|
-
AZURE_OPENAI = "
|
|
8
|
+
OPENAI = "OpenAI"
|
|
9
|
+
ANTHROPIC = "Anthropic"
|
|
10
|
+
AZURE_OPENAI = "Azure OpenAI"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@strawberry.type
|
|
14
14
|
class GenerativeProvider:
|
|
15
15
|
name: str
|
|
16
16
|
key: GenerativeProviderKey
|
|
17
|
+
|
|
18
|
+
@strawberry.field
|
|
19
|
+
async def dependencies(self) -> list[str]:
|
|
20
|
+
from phoenix.server.api.helpers.playground_registry import (
|
|
21
|
+
PLAYGROUND_CLIENT_REGISTRY,
|
|
22
|
+
PROVIDER_DEFAULT,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
default_client = PLAYGROUND_CLIENT_REGISTRY.get_client(self.key, PROVIDER_DEFAULT)
|
|
26
|
+
if default_client:
|
|
27
|
+
return default_client.dependencies()
|
|
28
|
+
return []
|
|
29
|
+
|
|
30
|
+
@strawberry.field
|
|
31
|
+
async def dependencies_installed(self) -> bool:
|
|
32
|
+
from phoenix.server.api.helpers.playground_registry import (
|
|
33
|
+
PLAYGROUND_CLIENT_REGISTRY,
|
|
34
|
+
PROVIDER_DEFAULT,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
default_client = PLAYGROUND_CLIENT_REGISTRY.get_client(self.key, PROVIDER_DEFAULT)
|
|
38
|
+
if default_client:
|
|
39
|
+
return default_client.dependencies_are_installed()
|
|
40
|
+
return False
|
phoenix/server/api/types/Span.py
CHANGED
|
@@ -20,10 +20,12 @@ from phoenix.server.api.helpers.dataset_helpers import (
|
|
|
20
20
|
get_dataset_example_input,
|
|
21
21
|
get_dataset_example_output,
|
|
22
22
|
)
|
|
23
|
+
from phoenix.server.api.input_types.InvocationParameters import InvocationParameter
|
|
23
24
|
from phoenix.server.api.input_types.SpanAnnotationSort import (
|
|
24
25
|
SpanAnnotationColumn,
|
|
25
26
|
SpanAnnotationSort,
|
|
26
27
|
)
|
|
28
|
+
from phoenix.server.api.types.GenerativeProvider import GenerativeProviderKey
|
|
27
29
|
from phoenix.server.api.types.SortDir import SortDir
|
|
28
30
|
from phoenix.server.api.types.SpanAnnotation import to_gql_span_annotation
|
|
29
31
|
from phoenix.trace.attributes import get_attribute_value
|
|
@@ -291,6 +293,41 @@ class Span(Node):
|
|
|
291
293
|
examples = await info.context.data_loaders.span_dataset_examples.load(self.id_attr)
|
|
292
294
|
return bool(examples)
|
|
293
295
|
|
|
296
|
+
@strawberry.field(description="Invocation parameters for the span") # type: ignore
|
|
297
|
+
async def invocation_parameters(self, info: Info[Context, None]) -> list[InvocationParameter]:
|
|
298
|
+
from phoenix.server.api.helpers.playground_clients import OpenAIStreamingClient
|
|
299
|
+
from phoenix.server.api.helpers.playground_registry import PLAYGROUND_CLIENT_REGISTRY
|
|
300
|
+
|
|
301
|
+
db_span = self.db_span
|
|
302
|
+
attributes = db_span.attributes
|
|
303
|
+
llm_provider: GenerativeProviderKey = (
|
|
304
|
+
get_attribute_value(attributes, SpanAttributes.LLM_PROVIDER)
|
|
305
|
+
or GenerativeProviderKey.OPENAI
|
|
306
|
+
)
|
|
307
|
+
llm_model = get_attribute_value(attributes, SpanAttributes.LLM_MODEL_NAME)
|
|
308
|
+
invocation_parameters = get_attribute_value(
|
|
309
|
+
attributes, SpanAttributes.LLM_INVOCATION_PARAMETERS
|
|
310
|
+
)
|
|
311
|
+
if invocation_parameters is None:
|
|
312
|
+
return []
|
|
313
|
+
invocation_parameters = json.loads(invocation_parameters)
|
|
314
|
+
# find the client class for the provider, if there is no client class or provider,
|
|
315
|
+
# return openai as default
|
|
316
|
+
client_class = PLAYGROUND_CLIENT_REGISTRY.get_client(llm_provider, llm_model)
|
|
317
|
+
if not client_class:
|
|
318
|
+
client_class = OpenAIStreamingClient
|
|
319
|
+
supported_invocation_parameters = client_class.supported_invocation_parameters()
|
|
320
|
+
# filter supported invocation parameters down to those whose canonical_name is in the
|
|
321
|
+
# invocation_parameters keys
|
|
322
|
+
return [
|
|
323
|
+
ip
|
|
324
|
+
for ip in supported_invocation_parameters
|
|
325
|
+
if (
|
|
326
|
+
ip.canonical_name in invocation_parameters
|
|
327
|
+
or ip.invocation_name in invocation_parameters
|
|
328
|
+
)
|
|
329
|
+
]
|
|
330
|
+
|
|
294
331
|
|
|
295
332
|
def to_gql_span(span: models.Span) -> Span:
|
|
296
333
|
events: list[SpanEvent] = list(map(SpanEvent.from_dict, span.events))
|
phoenix/server/app.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import contextlib
|
|
3
|
+
import importlib
|
|
3
4
|
import json
|
|
4
5
|
import logging
|
|
6
|
+
import os
|
|
5
7
|
from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence
|
|
6
8
|
from contextlib import AbstractAsyncContextManager, AsyncExitStack
|
|
7
9
|
from dataclasses import dataclass, field
|
|
@@ -24,6 +26,7 @@ import strawberry
|
|
|
24
26
|
from fastapi import APIRouter, Depends, FastAPI
|
|
25
27
|
from fastapi.middleware.gzip import GZipMiddleware
|
|
26
28
|
from fastapi.utils import is_body_allowed_for_status_code
|
|
29
|
+
from grpc.aio import ServerInterceptor
|
|
27
30
|
from sqlalchemy import select
|
|
28
31
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
|
29
32
|
from starlette.datastructures import State as StarletteState
|
|
@@ -40,7 +43,6 @@ from starlette.types import Scope, StatefulLifespan
|
|
|
40
43
|
from starlette.websockets import WebSocket
|
|
41
44
|
from strawberry.extensions import SchemaExtension
|
|
42
45
|
from strawberry.fastapi import GraphQLRouter
|
|
43
|
-
from strawberry.schema import BaseSchema
|
|
44
46
|
from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL
|
|
45
47
|
from typing_extensions import TypeAlias
|
|
46
48
|
|
|
@@ -51,6 +53,9 @@ from phoenix.config import (
|
|
|
51
53
|
SERVER_DIR,
|
|
52
54
|
OAuth2ClientConfig,
|
|
53
55
|
get_env_csrf_trusted_origins,
|
|
56
|
+
get_env_fastapi_middleware_paths,
|
|
57
|
+
get_env_gql_extension_paths,
|
|
58
|
+
get_env_grpc_interceptor_paths,
|
|
54
59
|
get_env_host,
|
|
55
60
|
get_env_port,
|
|
56
61
|
server_instrumentation_is_enabled,
|
|
@@ -98,7 +103,7 @@ from phoenix.server.api.routers import (
|
|
|
98
103
|
oauth2_router,
|
|
99
104
|
)
|
|
100
105
|
from phoenix.server.api.routers.v1 import REST_API_VERSION
|
|
101
|
-
from phoenix.server.api.schema import
|
|
106
|
+
from phoenix.server.api.schema import build_graphql_schema
|
|
102
107
|
from phoenix.server.bearer_auth import BearerTokenAuthBackend, is_authenticated
|
|
103
108
|
from phoenix.server.dml_event import DmlEvent
|
|
104
109
|
from phoenix.server.dml_event_handler import DmlEventHandler
|
|
@@ -150,6 +155,28 @@ ProjectName: TypeAlias = str
|
|
|
150
155
|
_Callback: TypeAlias = Callable[[], Union[None, Awaitable[None]]]
|
|
151
156
|
|
|
152
157
|
|
|
158
|
+
def import_object_from_file(file_path: str, object_name: str) -> Any:
|
|
159
|
+
"""Import an object (class or function) from a Python file."""
|
|
160
|
+
try:
|
|
161
|
+
if not os.path.isfile(file_path):
|
|
162
|
+
raise FileNotFoundError(f"File '{file_path}' does not exist.")
|
|
163
|
+
module_name = f"custom_module_{hash(file_path)}"
|
|
164
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
165
|
+
if spec is None:
|
|
166
|
+
raise ImportError(f"Could not load spec for '{file_path}'")
|
|
167
|
+
module = importlib.util.module_from_spec(spec)
|
|
168
|
+
loader = spec.loader
|
|
169
|
+
if loader is None:
|
|
170
|
+
raise ImportError(f"No loader found for '{file_path}'")
|
|
171
|
+
loader.exec_module(module)
|
|
172
|
+
try:
|
|
173
|
+
return getattr(module, object_name)
|
|
174
|
+
except AttributeError:
|
|
175
|
+
raise ImportError(f"Module '{file_path}' does not have an object '{object_name}'.")
|
|
176
|
+
except Exception as e:
|
|
177
|
+
raise ImportError(f"Could not import '{object_name}' from '{file_path}': {e}")
|
|
178
|
+
|
|
179
|
+
|
|
153
180
|
class OAuth2Idp(TypedDict):
|
|
154
181
|
name: str
|
|
155
182
|
displayName: str
|
|
@@ -166,6 +193,7 @@ class AppConfig(NamedTuple):
|
|
|
166
193
|
web_manifest_path: Path
|
|
167
194
|
authentication_enabled: bool
|
|
168
195
|
""" Whether authentication is enabled """
|
|
196
|
+
websockets_enabled: bool
|
|
169
197
|
oauth2_idps: Sequence[OAuth2Idp]
|
|
170
198
|
|
|
171
199
|
|
|
@@ -216,6 +244,7 @@ class Static(StaticFiles):
|
|
|
216
244
|
"manifest": self._web_manifest,
|
|
217
245
|
"authentication_enabled": self._app_config.authentication_enabled,
|
|
218
246
|
"oauth2_idps": self._app_config.oauth2_idps,
|
|
247
|
+
"websockets_enabled": self._app_config.websockets_enabled,
|
|
219
248
|
},
|
|
220
249
|
)
|
|
221
250
|
except Exception as e:
|
|
@@ -256,6 +285,39 @@ class HeadersMiddleware(BaseHTTPMiddleware):
|
|
|
256
285
|
return response
|
|
257
286
|
|
|
258
287
|
|
|
288
|
+
def user_fastapi_middlewares() -> list[Middleware]:
|
|
289
|
+
paths = get_env_fastapi_middleware_paths()
|
|
290
|
+
middlewares = []
|
|
291
|
+
for file_path, object_name in paths:
|
|
292
|
+
middleware_class = import_object_from_file(file_path, object_name)
|
|
293
|
+
if not issubclass(middleware_class, BaseHTTPMiddleware):
|
|
294
|
+
raise TypeError(f"{middleware_class} is not a subclass of BaseHTTPMiddleware")
|
|
295
|
+
middlewares.append(Middleware(middleware_class))
|
|
296
|
+
return middlewares
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def user_gql_extensions() -> list[Union[type[SchemaExtension], SchemaExtension]]:
|
|
300
|
+
paths = get_env_gql_extension_paths()
|
|
301
|
+
extensions = []
|
|
302
|
+
for file_path, object_name in paths:
|
|
303
|
+
extension_class = import_object_from_file(file_path, object_name)
|
|
304
|
+
if not issubclass(extension_class, SchemaExtension):
|
|
305
|
+
raise TypeError(f"{extension_class} is not a subclass of SchemaExtension")
|
|
306
|
+
extensions.append(extension_class)
|
|
307
|
+
return extensions
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def user_grpc_interceptors() -> list[ServerInterceptor]:
|
|
311
|
+
paths = get_env_grpc_interceptor_paths()
|
|
312
|
+
interceptors = []
|
|
313
|
+
for file_path, object_name in paths:
|
|
314
|
+
interceptor_class = import_object_from_file(file_path, object_name)
|
|
315
|
+
if not issubclass(interceptor_class, ServerInterceptor):
|
|
316
|
+
raise TypeError(f"{interceptor_class} is not a subclass of ServerInterceptor")
|
|
317
|
+
interceptors.append(interceptor_class)
|
|
318
|
+
return interceptors
|
|
319
|
+
|
|
320
|
+
|
|
259
321
|
ProjectRowId: TypeAlias = int
|
|
260
322
|
|
|
261
323
|
|
|
@@ -430,6 +492,7 @@ def _lifespan(
|
|
|
430
492
|
tracer_provider=tracer_provider,
|
|
431
493
|
enable_prometheus=enable_prometheus,
|
|
432
494
|
token_store=token_store,
|
|
495
|
+
interceptors=user_grpc_interceptors(),
|
|
433
496
|
)
|
|
434
497
|
await stack.enter_async_context(grpc_server)
|
|
435
498
|
await stack.enter_async_context(dml_event_handler)
|
|
@@ -463,7 +526,7 @@ async def check_healthz(_: Request) -> PlainTextResponse:
|
|
|
463
526
|
|
|
464
527
|
def create_graphql_router(
|
|
465
528
|
*,
|
|
466
|
-
|
|
529
|
+
graphql_schema: strawberry.Schema,
|
|
467
530
|
db: DbSessionFactory,
|
|
468
531
|
model: Model,
|
|
469
532
|
export_path: Path,
|
|
@@ -567,7 +630,7 @@ def create_graphql_router(
|
|
|
567
630
|
)
|
|
568
631
|
|
|
569
632
|
return GraphQLRouter(
|
|
570
|
-
|
|
633
|
+
graphql_schema,
|
|
571
634
|
graphql_ide="graphiql",
|
|
572
635
|
context_getter=get_context,
|
|
573
636
|
include_in_schema=False,
|
|
@@ -653,6 +716,7 @@ def create_app(
|
|
|
653
716
|
model: Model,
|
|
654
717
|
authentication_enabled: bool,
|
|
655
718
|
umap_params: UMAPParameters,
|
|
719
|
+
enable_websockets: bool,
|
|
656
720
|
corpus: Optional[Model] = None,
|
|
657
721
|
debug: bool = False,
|
|
658
722
|
dev: bool = False,
|
|
@@ -700,6 +764,7 @@ def create_app(
|
|
|
700
764
|
)
|
|
701
765
|
last_updated_at = LastUpdatedAt()
|
|
702
766
|
middlewares: list[Middleware] = [Middleware(HeadersMiddleware)]
|
|
767
|
+
middlewares.extend(user_fastapi_middlewares())
|
|
703
768
|
if origins := get_env_csrf_trusted_origins():
|
|
704
769
|
trusted_hostnames = [h for o in origins if o and (h := urlparse(o).hostname)]
|
|
705
770
|
middlewares.append(Middleware(RequestOriginHostnameValidator, trusted_hostnames))
|
|
@@ -733,8 +798,9 @@ def create_app(
|
|
|
733
798
|
initial_batch_of_evaluations=initial_batch_of_evaluations,
|
|
734
799
|
)
|
|
735
800
|
tracer_provider = None
|
|
736
|
-
|
|
737
|
-
|
|
801
|
+
graphql_schema_extensions: list[Union[type[SchemaExtension], SchemaExtension]] = []
|
|
802
|
+
graphql_schema_extensions.extend(user_gql_extensions())
|
|
803
|
+
|
|
738
804
|
if server_instrumentation_is_enabled():
|
|
739
805
|
tracer_provider = initialize_opentelemetry_tracer_provider()
|
|
740
806
|
from opentelemetry.trace import TracerProvider
|
|
@@ -752,16 +818,11 @@ def create_app(
|
|
|
752
818
|
# used by OpenInference.
|
|
753
819
|
self._tracer = cast(TracerProvider, tracer_provider).get_tracer("strawberry")
|
|
754
820
|
|
|
755
|
-
|
|
821
|
+
graphql_schema_extensions.append(_OpenTelemetryExtension)
|
|
756
822
|
|
|
757
823
|
graphql_router = create_graphql_router(
|
|
758
824
|
db=db,
|
|
759
|
-
|
|
760
|
-
query=schema.query,
|
|
761
|
-
mutation=schema.mutation,
|
|
762
|
-
subscription=schema.subscription,
|
|
763
|
-
extensions=strawberry_extensions,
|
|
764
|
-
),
|
|
825
|
+
graphql_schema=build_graphql_schema(graphql_schema_extensions),
|
|
765
826
|
model=model,
|
|
766
827
|
corpus=corpus,
|
|
767
828
|
authentication_enabled=authentication_enabled,
|
|
@@ -830,6 +891,7 @@ def create_app(
|
|
|
830
891
|
authentication_enabled=authentication_enabled,
|
|
831
892
|
web_manifest_path=web_manifest_path,
|
|
832
893
|
oauth2_idps=oauth2_idps,
|
|
894
|
+
websockets_enabled=enable_websockets,
|
|
833
895
|
),
|
|
834
896
|
),
|
|
835
897
|
name="static",
|
phoenix/server/grpc_server.py
CHANGED
|
@@ -56,6 +56,7 @@ class GrpcServer:
|
|
|
56
56
|
enable_prometheus: bool = False,
|
|
57
57
|
disabled: bool = False,
|
|
58
58
|
token_store: Optional[CanReadToken] = None,
|
|
59
|
+
interceptors: list[ServerInterceptor] = [],
|
|
59
60
|
) -> None:
|
|
60
61
|
self._callback = callback
|
|
61
62
|
self._server: Optional[Server] = None
|
|
@@ -63,11 +64,12 @@ class GrpcServer:
|
|
|
63
64
|
self._enable_prometheus = enable_prometheus
|
|
64
65
|
self._disabled = disabled
|
|
65
66
|
self._token_store = token_store
|
|
67
|
+
self._interceptors = interceptors
|
|
66
68
|
|
|
67
69
|
async def __aenter__(self) -> None:
|
|
70
|
+
interceptors = self._interceptors
|
|
68
71
|
if self._disabled:
|
|
69
72
|
return
|
|
70
|
-
interceptors: list[ServerInterceptor] = []
|
|
71
73
|
if self._token_store:
|
|
72
74
|
interceptors.append(ApiKeyInterceptor(self._token_store))
|
|
73
75
|
if self._enable_prometheus:
|
phoenix/server/main.py
CHANGED
|
@@ -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")
|
|
@@ -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,
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_components-
|
|
3
|
-
"file": "assets/components-
|
|
2
|
+
"_components-MllbfxfJ.js": {
|
|
3
|
+
"file": "assets/components-MllbfxfJ.js",
|
|
4
4
|
"name": "components",
|
|
5
5
|
"imports": [
|
|
6
|
-
"_vendor-
|
|
7
|
-
"_vendor-arizeai-
|
|
8
|
-
"_pages-
|
|
6
|
+
"_vendor-BEuNhfwH.js",
|
|
7
|
+
"_vendor-arizeai-Bskhzyjm.js",
|
|
8
|
+
"_pages-BHfC6jnL.js",
|
|
9
9
|
"_vendor-three-DwGkEfCM.js",
|
|
10
|
-
"_vendor-codemirror-
|
|
10
|
+
"_vendor-codemirror-DLlXCf0x.js"
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
|
-
"_pages-
|
|
14
|
-
"file": "assets/pages-
|
|
13
|
+
"_pages-BHfC6jnL.js": {
|
|
14
|
+
"file": "assets/pages-BHfC6jnL.js",
|
|
15
15
|
"name": "pages",
|
|
16
16
|
"imports": [
|
|
17
|
-
"_vendor-
|
|
18
|
-
"_vendor-arizeai-
|
|
19
|
-
"_components-
|
|
20
|
-
"_vendor-recharts-
|
|
21
|
-
"_vendor-codemirror-
|
|
17
|
+
"_vendor-BEuNhfwH.js",
|
|
18
|
+
"_vendor-arizeai-Bskhzyjm.js",
|
|
19
|
+
"_components-MllbfxfJ.js",
|
|
20
|
+
"_vendor-recharts-CRqhvLYg.js",
|
|
21
|
+
"_vendor-codemirror-DLlXCf0x.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-BEuNhfwH.js": {
|
|
29
|
+
"file": "assets/vendor-BEuNhfwH.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-Bskhzyjm.js": {
|
|
39
|
+
"file": "assets/vendor-arizeai-Bskhzyjm.js",
|
|
40
40
|
"name": "vendor-arizeai",
|
|
41
41
|
"imports": [
|
|
42
|
-
"_vendor-
|
|
42
|
+
"_vendor-BEuNhfwH.js"
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
|
-
"_vendor-codemirror-
|
|
46
|
-
"file": "assets/vendor-codemirror-
|
|
45
|
+
"_vendor-codemirror-DLlXCf0x.js": {
|
|
46
|
+
"file": "assets/vendor-codemirror-DLlXCf0x.js",
|
|
47
47
|
"name": "vendor-codemirror",
|
|
48
48
|
"imports": [
|
|
49
|
-
"_vendor-
|
|
49
|
+
"_vendor-BEuNhfwH.js"
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
|
-
"_vendor-recharts-
|
|
53
|
-
"file": "assets/vendor-recharts-
|
|
52
|
+
"_vendor-recharts-CRqhvLYg.js": {
|
|
53
|
+
"file": "assets/vendor-recharts-CRqhvLYg.js",
|
|
54
54
|
"name": "vendor-recharts",
|
|
55
55
|
"imports": [
|
|
56
|
-
"_vendor-
|
|
56
|
+
"_vendor-BEuNhfwH.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-BVO2YcT1.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-BEuNhfwH.js",
|
|
70
|
+
"_vendor-arizeai-Bskhzyjm.js",
|
|
71
|
+
"_pages-BHfC6jnL.js",
|
|
72
|
+
"_components-MllbfxfJ.js",
|
|
73
73
|
"_vendor-three-DwGkEfCM.js",
|
|
74
|
-
"_vendor-recharts-
|
|
75
|
-
"_vendor-codemirror-
|
|
74
|
+
"_vendor-recharts-CRqhvLYg.js",
|
|
75
|
+
"_vendor-codemirror-DLlXCf0x.js"
|
|
76
76
|
]
|
|
77
77
|
}
|
|
78
78
|
}
|