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
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
|
2
|
+
|
|
3
|
+
from phoenix.server.api.types.GenerativeProvider import GenerativeProviderKey
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from phoenix.server.api.helpers.playground_clients import PlaygroundStreamingClient
|
|
7
|
+
|
|
8
|
+
ModelName = Union[str, None]
|
|
9
|
+
ModelKey = tuple[GenerativeProviderKey, ModelName]
|
|
10
|
+
|
|
11
|
+
PROVIDER_DEFAULT = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SingletonMeta(type):
|
|
15
|
+
_instances: dict[Any, Any] = dict()
|
|
16
|
+
|
|
17
|
+
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
18
|
+
if cls not in cls._instances:
|
|
19
|
+
cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
|
|
20
|
+
return cls._instances[cls]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PlaygroundClientRegistry(metaclass=SingletonMeta):
|
|
24
|
+
def __init__(self) -> None:
|
|
25
|
+
self._registry: dict[
|
|
26
|
+
GenerativeProviderKey, dict[ModelName, Optional[type["PlaygroundStreamingClient"]]]
|
|
27
|
+
] = {}
|
|
28
|
+
|
|
29
|
+
def get_client(
|
|
30
|
+
self,
|
|
31
|
+
provider_key: GenerativeProviderKey,
|
|
32
|
+
model_name: ModelName,
|
|
33
|
+
) -> Optional[type["PlaygroundStreamingClient"]]:
|
|
34
|
+
provider_registry = self._registry.get(provider_key, {})
|
|
35
|
+
client_class = provider_registry.get(model_name)
|
|
36
|
+
if client_class is None and None in provider_registry:
|
|
37
|
+
client_class = provider_registry[PROVIDER_DEFAULT] # Fallback to provider default
|
|
38
|
+
return client_class
|
|
39
|
+
|
|
40
|
+
def list_all_providers(
|
|
41
|
+
self,
|
|
42
|
+
) -> list[GenerativeProviderKey]:
|
|
43
|
+
return [provider_key for provider_key in self._registry]
|
|
44
|
+
|
|
45
|
+
def list_models(self, provider_key: GenerativeProviderKey) -> list[str]:
|
|
46
|
+
provider_registry = self._registry.get(provider_key, {})
|
|
47
|
+
return [model_name for model_name in provider_registry.keys() if model_name is not None]
|
|
48
|
+
|
|
49
|
+
def list_all_models(self) -> list[ModelKey]:
|
|
50
|
+
return [
|
|
51
|
+
(provider_key, model_name)
|
|
52
|
+
for provider_key, provider_registry in self._registry.items()
|
|
53
|
+
for model_name in provider_registry.keys()
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
PLAYGROUND_CLIENT_REGISTRY: PlaygroundClientRegistry = PlaygroundClientRegistry()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def register_llm_client(
|
|
61
|
+
provider_key: GenerativeProviderKey,
|
|
62
|
+
model_names: list[ModelName],
|
|
63
|
+
) -> Callable[[type["PlaygroundStreamingClient"]], type["PlaygroundStreamingClient"]]:
|
|
64
|
+
def decorator(cls: type["PlaygroundStreamingClient"]) -> type["PlaygroundStreamingClient"]:
|
|
65
|
+
provider_registry = PLAYGROUND_CLIENT_REGISTRY._registry.setdefault(provider_key, {})
|
|
66
|
+
for model_name in model_names:
|
|
67
|
+
provider_registry[model_name] = cls
|
|
68
|
+
return cls
|
|
69
|
+
|
|
70
|
+
return decorator
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import asdict
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from itertools import chain
|
|
7
|
+
from traceback import format_exc
|
|
8
|
+
from types import TracebackType
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Iterable,
|
|
12
|
+
Iterator,
|
|
13
|
+
Optional,
|
|
14
|
+
Union,
|
|
15
|
+
cast,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from openinference.instrumentation import safe_json_dumps
|
|
19
|
+
from openinference.semconv.trace import (
|
|
20
|
+
MessageAttributes,
|
|
21
|
+
OpenInferenceMimeTypeValues,
|
|
22
|
+
OpenInferenceSpanKindValues,
|
|
23
|
+
SpanAttributes,
|
|
24
|
+
ToolAttributes,
|
|
25
|
+
ToolCallAttributes,
|
|
26
|
+
)
|
|
27
|
+
from opentelemetry.sdk.trace.id_generator import RandomIdGenerator as DefaultOTelIDGenerator
|
|
28
|
+
from opentelemetry.trace import StatusCode
|
|
29
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
30
|
+
from strawberry.scalars import JSON as JSONScalarType
|
|
31
|
+
from typing_extensions import Self, TypeAlias, assert_never
|
|
32
|
+
|
|
33
|
+
from phoenix.datetime_utils import local_now, normalize_datetime
|
|
34
|
+
from phoenix.db import models
|
|
35
|
+
from phoenix.server.api.input_types.ChatCompletionInput import (
|
|
36
|
+
ChatCompletionInput,
|
|
37
|
+
ChatCompletionOverDatasetInput,
|
|
38
|
+
)
|
|
39
|
+
from phoenix.server.api.types.ChatCompletionMessageRole import ChatCompletionMessageRole
|
|
40
|
+
from phoenix.server.api.types.ChatCompletionSubscriptionPayload import (
|
|
41
|
+
TextChunk,
|
|
42
|
+
ToolCallChunk,
|
|
43
|
+
)
|
|
44
|
+
from phoenix.trace.attributes import unflatten
|
|
45
|
+
from phoenix.trace.schemas import (
|
|
46
|
+
SpanEvent,
|
|
47
|
+
SpanException,
|
|
48
|
+
)
|
|
49
|
+
from phoenix.utilities.json import jsonify
|
|
50
|
+
|
|
51
|
+
ChatCompletionMessage: TypeAlias = tuple[
|
|
52
|
+
ChatCompletionMessageRole, str, Optional[str], Optional[list[str]]
|
|
53
|
+
]
|
|
54
|
+
ToolCallID: TypeAlias = str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class streaming_llm_span:
|
|
58
|
+
"""
|
|
59
|
+
Creates an LLM span for a streaming chat completion.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
*,
|
|
65
|
+
input: Union[ChatCompletionInput, ChatCompletionOverDatasetInput],
|
|
66
|
+
messages: list[ChatCompletionMessage],
|
|
67
|
+
invocation_parameters: Mapping[str, Any],
|
|
68
|
+
attributes: Optional[dict[str, Any]] = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
self._input = input
|
|
71
|
+
self._attributes: dict[str, Any] = attributes if attributes is not None else {}
|
|
72
|
+
self._attributes.update(
|
|
73
|
+
chain(
|
|
74
|
+
_llm_span_kind(),
|
|
75
|
+
_llm_model_name(input.model.name),
|
|
76
|
+
_llm_tools(input.tools or []),
|
|
77
|
+
_llm_input_messages(messages),
|
|
78
|
+
_llm_invocation_parameters(invocation_parameters),
|
|
79
|
+
_input_value_and_mime_type(input),
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
self._events: list[SpanEvent] = []
|
|
83
|
+
self._start_time: datetime
|
|
84
|
+
self._end_time: datetime
|
|
85
|
+
self._response_chunks: list[Union[TextChunk, ToolCallChunk]] = []
|
|
86
|
+
self._text_chunks: list[TextChunk] = []
|
|
87
|
+
self._tool_call_chunks: defaultdict[ToolCallID, list[ToolCallChunk]] = defaultdict(list)
|
|
88
|
+
self._status_code: StatusCode
|
|
89
|
+
self._status_message: str
|
|
90
|
+
self._db_span: models.Span
|
|
91
|
+
self._db_trace: models.Trace
|
|
92
|
+
|
|
93
|
+
async def __aenter__(self) -> Self:
|
|
94
|
+
self._start_time = cast(datetime, normalize_datetime(dt=local_now(), tz=timezone.utc))
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
async def __aexit__(
|
|
98
|
+
self,
|
|
99
|
+
exc_type: Optional[type[BaseException]],
|
|
100
|
+
exc_value: Optional[BaseException],
|
|
101
|
+
traceback: Optional[TracebackType],
|
|
102
|
+
) -> bool:
|
|
103
|
+
self._end_time = cast(datetime, normalize_datetime(dt=local_now(), tz=timezone.utc))
|
|
104
|
+
self._status_code = StatusCode.OK
|
|
105
|
+
self._status_message = ""
|
|
106
|
+
if exc_type is not None:
|
|
107
|
+
self._status_code = StatusCode.ERROR
|
|
108
|
+
self._status_message = str(exc_value)
|
|
109
|
+
self._events.append(
|
|
110
|
+
SpanException(
|
|
111
|
+
timestamp=self._end_time,
|
|
112
|
+
message=self._status_message,
|
|
113
|
+
exception_type=type(exc_value).__name__,
|
|
114
|
+
exception_escaped=False,
|
|
115
|
+
exception_stacktrace=format_exc(),
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
if self._response_chunks:
|
|
119
|
+
self._attributes.update(
|
|
120
|
+
chain(
|
|
121
|
+
_output_value_and_mime_type(self._response_chunks),
|
|
122
|
+
_llm_output_messages(self._text_chunks, self._tool_call_chunks),
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
def set_attributes(self, attributes: Mapping[str, Any]) -> None:
|
|
128
|
+
self._attributes.update(attributes)
|
|
129
|
+
|
|
130
|
+
def add_to_session(
|
|
131
|
+
self,
|
|
132
|
+
session: AsyncSession,
|
|
133
|
+
project_id: int,
|
|
134
|
+
) -> models.Span:
|
|
135
|
+
prompt_tokens = self._attributes.get(LLM_TOKEN_COUNT_PROMPT, 0)
|
|
136
|
+
completion_tokens = self._attributes.get(LLM_TOKEN_COUNT_COMPLETION, 0)
|
|
137
|
+
trace_id = _generate_trace_id()
|
|
138
|
+
span_id = _generate_span_id()
|
|
139
|
+
self._db_trace = models.Trace(
|
|
140
|
+
project_rowid=project_id,
|
|
141
|
+
trace_id=trace_id,
|
|
142
|
+
start_time=self._start_time,
|
|
143
|
+
end_time=self._end_time,
|
|
144
|
+
)
|
|
145
|
+
self._db_span = models.Span(
|
|
146
|
+
trace_rowid=self._db_trace.id,
|
|
147
|
+
span_id=span_id,
|
|
148
|
+
parent_id=None,
|
|
149
|
+
name="ChatCompletion",
|
|
150
|
+
span_kind=LLM,
|
|
151
|
+
start_time=self._start_time,
|
|
152
|
+
end_time=self._end_time,
|
|
153
|
+
attributes=unflatten(self._attributes.items()),
|
|
154
|
+
events=[_serialize_event(event) for event in self._events],
|
|
155
|
+
status_code=self._status_code.name,
|
|
156
|
+
status_message=self._status_message,
|
|
157
|
+
cumulative_error_count=int(self._status_code is StatusCode.ERROR),
|
|
158
|
+
cumulative_llm_token_count_prompt=prompt_tokens,
|
|
159
|
+
cumulative_llm_token_count_completion=completion_tokens,
|
|
160
|
+
llm_token_count_prompt=prompt_tokens,
|
|
161
|
+
llm_token_count_completion=completion_tokens,
|
|
162
|
+
trace=self._db_trace,
|
|
163
|
+
)
|
|
164
|
+
session.add(self._db_trace)
|
|
165
|
+
session.add(self._db_span)
|
|
166
|
+
return self._db_span
|
|
167
|
+
|
|
168
|
+
def add_response_chunk(self, chunk: Union[TextChunk, ToolCallChunk]) -> None:
|
|
169
|
+
self._response_chunks.append(chunk)
|
|
170
|
+
if isinstance(chunk, TextChunk):
|
|
171
|
+
self._text_chunks.append(chunk)
|
|
172
|
+
elif isinstance(chunk, ToolCallChunk):
|
|
173
|
+
self._tool_call_chunks[chunk.id].append(chunk)
|
|
174
|
+
else:
|
|
175
|
+
assert_never(chunk)
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def start_time(self) -> datetime:
|
|
179
|
+
return self._db_span.start_time
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def end_time(self) -> datetime:
|
|
183
|
+
return self._db_span.end_time
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def error_message(self) -> Optional[str]:
|
|
187
|
+
return self._status_message if self._status_code is StatusCode.ERROR else None
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def trace_id(self) -> str:
|
|
191
|
+
return self._db_trace.trace_id
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def attributes(self) -> dict[str, Any]:
|
|
195
|
+
return self._db_span.attributes
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _llm_span_kind() -> Iterator[tuple[str, Any]]:
|
|
199
|
+
yield OPENINFERENCE_SPAN_KIND, LLM
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _llm_model_name(model_name: str) -> Iterator[tuple[str, Any]]:
|
|
203
|
+
yield LLM_MODEL_NAME, model_name
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _llm_invocation_parameters(
|
|
207
|
+
invocation_parameters: Mapping[str, Any],
|
|
208
|
+
) -> Iterator[tuple[str, Any]]:
|
|
209
|
+
if invocation_parameters:
|
|
210
|
+
yield LLM_INVOCATION_PARAMETERS, safe_json_dumps(invocation_parameters)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _llm_tools(tools: list[JSONScalarType]) -> Iterator[tuple[str, Any]]:
|
|
214
|
+
for tool_index, tool in enumerate(tools):
|
|
215
|
+
yield f"{LLM_TOOLS}.{tool_index}.{TOOL_JSON_SCHEMA}", json.dumps(tool)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _input_value_and_mime_type(input: Any) -> Iterator[tuple[str, Any]]:
|
|
219
|
+
assert (api_key := "api_key") in (input_data := jsonify(input))
|
|
220
|
+
disallowed_keys = {"api_key", "invocation_parameters"}
|
|
221
|
+
input_data = {k: v for k, v in input_data.items() if k not in disallowed_keys}
|
|
222
|
+
assert api_key not in input_data
|
|
223
|
+
yield INPUT_MIME_TYPE, JSON
|
|
224
|
+
yield INPUT_VALUE, safe_json_dumps(input_data)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _output_value_and_mime_type(output: Any) -> Iterator[tuple[str, Any]]:
|
|
228
|
+
yield OUTPUT_MIME_TYPE, JSON
|
|
229
|
+
yield OUTPUT_VALUE, safe_json_dumps(jsonify(output))
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _llm_input_messages(
|
|
233
|
+
messages: Iterable[
|
|
234
|
+
tuple[ChatCompletionMessageRole, str, Optional[str], Optional[list[JSONScalarType]]]
|
|
235
|
+
],
|
|
236
|
+
) -> Iterator[tuple[str, Any]]:
|
|
237
|
+
for i, (role, content, _tool_call_id, tool_calls) in enumerate(messages):
|
|
238
|
+
yield f"{LLM_INPUT_MESSAGES}.{i}.{MESSAGE_ROLE}", role.value.lower()
|
|
239
|
+
yield f"{LLM_INPUT_MESSAGES}.{i}.{MESSAGE_CONTENT}", content
|
|
240
|
+
if tool_calls is not None:
|
|
241
|
+
for tool_call_index, tool_call in enumerate(tool_calls):
|
|
242
|
+
yield (
|
|
243
|
+
f"{LLM_INPUT_MESSAGES}.{i}.{MESSAGE_TOOL_CALLS}.{tool_call_index}.{TOOL_CALL_FUNCTION_NAME}",
|
|
244
|
+
tool_call["function"]["name"],
|
|
245
|
+
)
|
|
246
|
+
if arguments := tool_call["function"]["arguments"]:
|
|
247
|
+
yield (
|
|
248
|
+
f"{LLM_INPUT_MESSAGES}.{i}.{MESSAGE_TOOL_CALLS}.{tool_call_index}.{TOOL_CALL_FUNCTION_ARGUMENTS_JSON}",
|
|
249
|
+
safe_json_dumps(jsonify(arguments)),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _llm_output_messages(
|
|
254
|
+
text_chunks: list[TextChunk],
|
|
255
|
+
tool_call_chunks: defaultdict[ToolCallID, list[ToolCallChunk]],
|
|
256
|
+
) -> Iterator[tuple[str, Any]]:
|
|
257
|
+
yield f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_ROLE}", "assistant"
|
|
258
|
+
if content := "".join(chunk.content for chunk in text_chunks):
|
|
259
|
+
yield f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_CONTENT}", content
|
|
260
|
+
for tool_call_index, (_tool_call_id, tool_call_chunks_) in enumerate(tool_call_chunks.items()):
|
|
261
|
+
if tool_call_chunks_ and (name := tool_call_chunks_[0].function.name):
|
|
262
|
+
yield (
|
|
263
|
+
f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_TOOL_CALLS}.{tool_call_index}.{TOOL_CALL_FUNCTION_NAME}",
|
|
264
|
+
name,
|
|
265
|
+
)
|
|
266
|
+
if arguments := "".join(chunk.function.arguments for chunk in tool_call_chunks_):
|
|
267
|
+
yield (
|
|
268
|
+
f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_TOOL_CALLS}.{tool_call_index}.{TOOL_CALL_FUNCTION_ARGUMENTS_JSON}",
|
|
269
|
+
arguments,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _generate_trace_id() -> str:
|
|
274
|
+
"""
|
|
275
|
+
Generates a random trace ID in hexadecimal format.
|
|
276
|
+
"""
|
|
277
|
+
return _hex(DefaultOTelIDGenerator().generate_trace_id())
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _generate_span_id() -> str:
|
|
281
|
+
"""
|
|
282
|
+
Generates a random span ID in hexadecimal format.
|
|
283
|
+
"""
|
|
284
|
+
return _hex(DefaultOTelIDGenerator().generate_span_id())
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _hex(number: int) -> str:
|
|
288
|
+
"""
|
|
289
|
+
Converts an integer to a hexadecimal string.
|
|
290
|
+
"""
|
|
291
|
+
return hex(number)[2:]
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _serialize_event(event: SpanEvent) -> dict[str, Any]:
|
|
295
|
+
"""
|
|
296
|
+
Serializes a SpanEvent to a dictionary.
|
|
297
|
+
"""
|
|
298
|
+
return {k: (v.isoformat() if isinstance(v, datetime) else v) for k, v in asdict(event).items()}
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
JSON = OpenInferenceMimeTypeValues.JSON.value
|
|
302
|
+
|
|
303
|
+
LLM = OpenInferenceSpanKindValues.LLM.value
|
|
304
|
+
|
|
305
|
+
OPENINFERENCE_SPAN_KIND = SpanAttributes.OPENINFERENCE_SPAN_KIND
|
|
306
|
+
INPUT_MIME_TYPE = SpanAttributes.INPUT_MIME_TYPE
|
|
307
|
+
INPUT_VALUE = SpanAttributes.INPUT_VALUE
|
|
308
|
+
OUTPUT_MIME_TYPE = SpanAttributes.OUTPUT_MIME_TYPE
|
|
309
|
+
OUTPUT_VALUE = SpanAttributes.OUTPUT_VALUE
|
|
310
|
+
LLM_INPUT_MESSAGES = SpanAttributes.LLM_INPUT_MESSAGES
|
|
311
|
+
LLM_OUTPUT_MESSAGES = SpanAttributes.LLM_OUTPUT_MESSAGES
|
|
312
|
+
LLM_MODEL_NAME = SpanAttributes.LLM_MODEL_NAME
|
|
313
|
+
LLM_INVOCATION_PARAMETERS = SpanAttributes.LLM_INVOCATION_PARAMETERS
|
|
314
|
+
LLM_TOOLS = SpanAttributes.LLM_TOOLS
|
|
315
|
+
LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT
|
|
316
|
+
LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION
|
|
317
|
+
|
|
318
|
+
MESSAGE_CONTENT = MessageAttributes.MESSAGE_CONTENT
|
|
319
|
+
MESSAGE_ROLE = MessageAttributes.MESSAGE_ROLE
|
|
320
|
+
MESSAGE_TOOL_CALLS = MessageAttributes.MESSAGE_TOOL_CALLS
|
|
321
|
+
|
|
322
|
+
TOOL_CALL_FUNCTION_NAME = ToolCallAttributes.TOOL_CALL_FUNCTION_NAME
|
|
323
|
+
TOOL_CALL_FUNCTION_ARGUMENTS_JSON = ToolCallAttributes.TOOL_CALL_FUNCTION_ARGUMENTS_JSON
|
|
324
|
+
|
|
325
|
+
TOOL_JSON_SCHEMA = ToolAttributes.TOOL_JSON_SCHEMA
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
import strawberry
|
|
4
4
|
from strawberry import UNSET
|
|
@@ -11,6 +11,6 @@ from .DatasetExampleInput import DatasetExampleInput
|
|
|
11
11
|
@strawberry.input
|
|
12
12
|
class AddExamplesToDatasetInput:
|
|
13
13
|
dataset_id: GlobalID
|
|
14
|
-
examples:
|
|
14
|
+
examples: list[DatasetExampleInput]
|
|
15
15
|
dataset_version_description: Optional[str] = UNSET
|
|
16
16
|
dataset_version_metadata: Optional[JSON] = UNSET
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
import strawberry
|
|
4
4
|
from strawberry import UNSET
|
|
@@ -9,6 +9,6 @@ from strawberry.scalars import JSON
|
|
|
9
9
|
@strawberry.input
|
|
10
10
|
class AddSpansToDatasetInput:
|
|
11
11
|
dataset_id: GlobalID
|
|
12
|
-
span_ids:
|
|
12
|
+
span_ids: list[GlobalID]
|
|
13
13
|
dataset_version_description: Optional[str] = UNSET
|
|
14
14
|
dataset_version_metadata: Optional[JSON] = UNSET
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import strawberry
|
|
4
|
+
from strawberry import UNSET
|
|
5
|
+
from strawberry.relay.types import GlobalID
|
|
6
|
+
from strawberry.scalars import JSON
|
|
7
|
+
|
|
8
|
+
from phoenix.server.api.types.TemplateLanguage import TemplateLanguage
|
|
9
|
+
|
|
10
|
+
from .ChatCompletionMessageInput import ChatCompletionMessageInput
|
|
11
|
+
from .GenerativeModelInput import GenerativeModelInput
|
|
12
|
+
from .InvocationParameters import InvocationParameterInput
|
|
13
|
+
from .TemplateOptions import TemplateOptions
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@strawberry.input
|
|
17
|
+
class ChatCompletionInput:
|
|
18
|
+
messages: list[ChatCompletionMessageInput]
|
|
19
|
+
model: GenerativeModelInput
|
|
20
|
+
invocation_parameters: list[InvocationParameterInput] = strawberry.field(default_factory=list)
|
|
21
|
+
tools: Optional[list[JSON]] = UNSET
|
|
22
|
+
api_key: Optional[str] = strawberry.field(default=None)
|
|
23
|
+
template: Optional[TemplateOptions] = UNSET
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@strawberry.input
|
|
27
|
+
class ChatCompletionOverDatasetInput:
|
|
28
|
+
messages: list[ChatCompletionMessageInput]
|
|
29
|
+
model: GenerativeModelInput
|
|
30
|
+
invocation_parameters: list[InvocationParameterInput] = strawberry.field(default_factory=list)
|
|
31
|
+
tools: Optional[list[JSON]] = UNSET
|
|
32
|
+
api_key: Optional[str] = strawberry.field(default=None)
|
|
33
|
+
template_language: TemplateLanguage
|
|
34
|
+
dataset_id: GlobalID
|
|
35
|
+
dataset_version_id: Optional[GlobalID] = None
|
|
36
|
+
experiment_name: Optional[str] = None
|
|
37
|
+
experiment_description: Optional[str] = None
|
|
38
|
+
experiment_metadata: Optional[JSON] = strawberry.field(default_factory=dict)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
1
3
|
import strawberry
|
|
4
|
+
from strawberry import UNSET
|
|
2
5
|
from strawberry.scalars import JSON
|
|
3
6
|
|
|
4
7
|
from phoenix.server.api.types.ChatCompletionMessageRole import ChatCompletionMessageRole
|
|
@@ -8,5 +11,14 @@ from phoenix.server.api.types.ChatCompletionMessageRole import ChatCompletionMes
|
|
|
8
11
|
class ChatCompletionMessageInput:
|
|
9
12
|
role: ChatCompletionMessageRole
|
|
10
13
|
content: JSON = strawberry.field(
|
|
11
|
-
|
|
14
|
+
default="",
|
|
15
|
+
description="The content of the message as JSON to support various kinds of text",
|
|
16
|
+
)
|
|
17
|
+
tool_calls: Optional[list[JSON]] = strawberry.field(
|
|
18
|
+
description="The tool calls that were made in the message",
|
|
19
|
+
default=UNSET,
|
|
20
|
+
)
|
|
21
|
+
tool_call_id: Optional[str] = strawberry.field(
|
|
22
|
+
description="The ID that corresponds to a prior tool call. Used to link a tool message to a pre-existing tool call.", # noqa: E501
|
|
23
|
+
default=UNSET,
|
|
12
24
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
import strawberry
|
|
4
4
|
from strawberry import ID, UNSET
|
|
@@ -6,5 +6,5 @@ from strawberry import ID, UNSET
|
|
|
6
6
|
|
|
7
7
|
@strawberry.input
|
|
8
8
|
class ClusterInput:
|
|
9
|
-
event_ids:
|
|
9
|
+
event_ids: list[ID]
|
|
10
10
|
id: Optional[ID] = UNSET
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
import strawberry
|
|
4
4
|
from strawberry import UNSET
|
|
@@ -8,6 +8,6 @@ from strawberry.scalars import JSON
|
|
|
8
8
|
|
|
9
9
|
@strawberry.input
|
|
10
10
|
class DeleteDatasetExamplesInput:
|
|
11
|
-
example_ids:
|
|
11
|
+
example_ids: list[GlobalID]
|
|
12
12
|
dataset_version_description: Optional[str] = UNSET
|
|
13
13
|
dataset_version_metadata: Optional[JSON] = UNSET
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
import strawberry
|
|
4
4
|
from strawberry import UNSET
|
|
@@ -52,9 +52,9 @@ class DimensionFilter:
|
|
|
52
52
|
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
|
-
types: Optional[
|
|
56
|
-
shapes: Optional[
|
|
57
|
-
data_types: Optional[
|
|
55
|
+
types: Optional[list[DimensionType]] = UNSET
|
|
56
|
+
shapes: Optional[list[DimensionShape]] = UNSET
|
|
57
|
+
data_types: Optional[list[DimensionDataType]] = UNSET
|
|
58
58
|
|
|
59
59
|
def __post_init__(self) -> None:
|
|
60
60
|
self.types = ensure_list(self.types)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import strawberry
|
|
4
|
+
from strawberry import UNSET
|
|
5
|
+
|
|
6
|
+
from phoenix.server.api.types.GenerativeProvider import GenerativeProviderKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@strawberry.input
|
|
10
|
+
class GenerativeModelInput:
|
|
11
|
+
provider_key: GenerativeProviderKey
|
|
12
|
+
name: str
|
|
13
|
+
""" The name of the model. Or the Deployment Name for Azure OpenAI models. """
|
|
14
|
+
endpoint: Optional[str] = UNSET
|
|
15
|
+
""" The endpoint to use for the model. Only required for Azure OpenAI models. """
|
|
16
|
+
api_version: Optional[str] = UNSET
|
|
17
|
+
""" The API version to use for the model. """
|