arize-phoenix 3.16.0__py3-none-any.whl → 7.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-7.7.0.dist-info/METADATA +261 -0
- arize_phoenix-7.7.0.dist-info/RECORD +345 -0
- {arize_phoenix-3.16.0.dist-info → arize_phoenix-7.7.0.dist-info}/WHEEL +1 -1
- arize_phoenix-7.7.0.dist-info/entry_points.txt +3 -0
- phoenix/__init__.py +86 -14
- phoenix/auth.py +309 -0
- phoenix/config.py +675 -45
- phoenix/core/model.py +32 -30
- phoenix/core/model_schema.py +102 -109
- phoenix/core/model_schema_adapter.py +48 -45
- phoenix/datetime_utils.py +24 -3
- phoenix/db/README.md +54 -0
- phoenix/db/__init__.py +4 -0
- phoenix/db/alembic.ini +85 -0
- phoenix/db/bulk_inserter.py +294 -0
- phoenix/db/engines.py +208 -0
- phoenix/db/enums.py +20 -0
- phoenix/db/facilitator.py +113 -0
- phoenix/db/helpers.py +159 -0
- phoenix/db/insertion/constants.py +2 -0
- phoenix/db/insertion/dataset.py +227 -0
- phoenix/db/insertion/document_annotation.py +171 -0
- phoenix/db/insertion/evaluation.py +191 -0
- phoenix/db/insertion/helpers.py +98 -0
- phoenix/db/insertion/span.py +193 -0
- phoenix/db/insertion/span_annotation.py +158 -0
- phoenix/db/insertion/trace_annotation.py +158 -0
- phoenix/db/insertion/types.py +256 -0
- phoenix/db/migrate.py +86 -0
- phoenix/db/migrations/data_migration_scripts/populate_project_sessions.py +199 -0
- phoenix/db/migrations/env.py +114 -0
- phoenix/db/migrations/script.py.mako +26 -0
- phoenix/db/migrations/versions/10460e46d750_datasets.py +317 -0
- phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py +126 -0
- phoenix/db/migrations/versions/4ded9e43755f_create_project_sessions_table.py +66 -0
- phoenix/db/migrations/versions/cd164e83824f_users_and_tokens.py +157 -0
- phoenix/db/migrations/versions/cf03bd6bae1d_init.py +280 -0
- phoenix/db/models.py +807 -0
- phoenix/exceptions.py +5 -1
- phoenix/experiments/__init__.py +6 -0
- phoenix/experiments/evaluators/__init__.py +29 -0
- phoenix/experiments/evaluators/base.py +158 -0
- phoenix/experiments/evaluators/code_evaluators.py +184 -0
- phoenix/experiments/evaluators/llm_evaluators.py +473 -0
- phoenix/experiments/evaluators/utils.py +236 -0
- phoenix/experiments/functions.py +772 -0
- phoenix/experiments/tracing.py +86 -0
- phoenix/experiments/types.py +726 -0
- phoenix/experiments/utils.py +25 -0
- phoenix/inferences/__init__.py +0 -0
- phoenix/{datasets → inferences}/errors.py +6 -5
- phoenix/{datasets → inferences}/fixtures.py +49 -42
- phoenix/{datasets/dataset.py → inferences/inferences.py} +121 -105
- phoenix/{datasets → inferences}/schema.py +11 -11
- phoenix/{datasets → inferences}/validation.py +13 -14
- phoenix/logging/__init__.py +3 -0
- phoenix/logging/_config.py +90 -0
- phoenix/logging/_filter.py +6 -0
- phoenix/logging/_formatter.py +69 -0
- phoenix/metrics/__init__.py +5 -4
- phoenix/metrics/binning.py +4 -3
- 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 +9 -3
- phoenix/pointcloud/clustering.py +5 -5
- phoenix/pointcloud/pointcloud.py +7 -5
- phoenix/pointcloud/projectors.py +5 -6
- phoenix/pointcloud/umap_parameters.py +53 -52
- phoenix/server/api/README.md +28 -0
- phoenix/server/api/auth.py +44 -0
- phoenix/server/api/context.py +152 -9
- phoenix/server/api/dataloaders/__init__.py +91 -0
- phoenix/server/api/dataloaders/annotation_summaries.py +139 -0
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +54 -0
- phoenix/server/api/dataloaders/cache/__init__.py +3 -0
- phoenix/server/api/dataloaders/cache/two_tier_cache.py +68 -0
- phoenix/server/api/dataloaders/dataset_example_revisions.py +131 -0
- phoenix/server/api/dataloaders/dataset_example_spans.py +38 -0
- phoenix/server/api/dataloaders/document_evaluation_summaries.py +144 -0
- phoenix/server/api/dataloaders/document_evaluations.py +31 -0
- phoenix/server/api/dataloaders/document_retrieval_metrics.py +89 -0
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +79 -0
- phoenix/server/api/dataloaders/experiment_error_rates.py +58 -0
- phoenix/server/api/dataloaders/experiment_run_annotations.py +36 -0
- phoenix/server/api/dataloaders/experiment_run_counts.py +49 -0
- phoenix/server/api/dataloaders/experiment_sequence_number.py +44 -0
- phoenix/server/api/dataloaders/latency_ms_quantile.py +188 -0
- phoenix/server/api/dataloaders/min_start_or_max_end_times.py +85 -0
- phoenix/server/api/dataloaders/project_by_name.py +31 -0
- phoenix/server/api/dataloaders/record_counts.py +116 -0
- phoenix/server/api/dataloaders/session_io.py +79 -0
- phoenix/server/api/dataloaders/session_num_traces.py +30 -0
- phoenix/server/api/dataloaders/session_num_traces_with_error.py +32 -0
- phoenix/server/api/dataloaders/session_token_usages.py +41 -0
- phoenix/server/api/dataloaders/session_trace_latency_ms_quantile.py +55 -0
- phoenix/server/api/dataloaders/span_annotations.py +26 -0
- phoenix/server/api/dataloaders/span_dataset_examples.py +31 -0
- phoenix/server/api/dataloaders/span_descendants.py +57 -0
- phoenix/server/api/dataloaders/span_projects.py +33 -0
- phoenix/server/api/dataloaders/token_counts.py +124 -0
- phoenix/server/api/dataloaders/trace_by_trace_ids.py +25 -0
- phoenix/server/api/dataloaders/trace_root_spans.py +32 -0
- phoenix/server/api/dataloaders/user_roles.py +30 -0
- phoenix/server/api/dataloaders/users.py +33 -0
- phoenix/server/api/exceptions.py +48 -0
- phoenix/server/api/helpers/__init__.py +12 -0
- phoenix/server/api/helpers/dataset_helpers.py +217 -0
- phoenix/server/api/helpers/experiment_run_filters.py +763 -0
- phoenix/server/api/helpers/playground_clients.py +948 -0
- phoenix/server/api/helpers/playground_registry.py +70 -0
- phoenix/server/api/helpers/playground_spans.py +455 -0
- phoenix/server/api/input_types/AddExamplesToDatasetInput.py +16 -0
- phoenix/server/api/input_types/AddSpansToDatasetInput.py +14 -0
- phoenix/server/api/input_types/ChatCompletionInput.py +38 -0
- phoenix/server/api/input_types/ChatCompletionMessageInput.py +24 -0
- phoenix/server/api/input_types/ClearProjectInput.py +15 -0
- phoenix/server/api/input_types/ClusterInput.py +2 -2
- phoenix/server/api/input_types/CreateDatasetInput.py +12 -0
- phoenix/server/api/input_types/CreateSpanAnnotationInput.py +18 -0
- phoenix/server/api/input_types/CreateTraceAnnotationInput.py +18 -0
- phoenix/server/api/input_types/DataQualityMetricInput.py +5 -2
- phoenix/server/api/input_types/DatasetExampleInput.py +14 -0
- phoenix/server/api/input_types/DatasetSort.py +17 -0
- phoenix/server/api/input_types/DatasetVersionSort.py +16 -0
- phoenix/server/api/input_types/DeleteAnnotationsInput.py +7 -0
- phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +13 -0
- phoenix/server/api/input_types/DeleteDatasetInput.py +7 -0
- phoenix/server/api/input_types/DeleteExperimentsInput.py +7 -0
- 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 +162 -0
- phoenix/server/api/input_types/PatchAnnotationInput.py +19 -0
- phoenix/server/api/input_types/PatchDatasetExamplesInput.py +35 -0
- phoenix/server/api/input_types/PatchDatasetInput.py +14 -0
- phoenix/server/api/input_types/PerformanceMetricInput.py +5 -2
- phoenix/server/api/input_types/ProjectSessionSort.py +29 -0
- phoenix/server/api/input_types/SpanAnnotationSort.py +17 -0
- phoenix/server/api/input_types/SpanSort.py +134 -69
- phoenix/server/api/input_types/TemplateOptions.py +10 -0
- phoenix/server/api/input_types/TraceAnnotationSort.py +17 -0
- phoenix/server/api/input_types/UserRoleInput.py +9 -0
- phoenix/server/api/mutations/__init__.py +28 -0
- phoenix/server/api/mutations/api_key_mutations.py +167 -0
- phoenix/server/api/mutations/chat_mutations.py +593 -0
- phoenix/server/api/mutations/dataset_mutations.py +591 -0
- phoenix/server/api/mutations/experiment_mutations.py +75 -0
- phoenix/server/api/{types/ExportEventsMutation.py → mutations/export_events_mutations.py} +21 -18
- phoenix/server/api/mutations/project_mutations.py +57 -0
- phoenix/server/api/mutations/span_annotations_mutations.py +128 -0
- phoenix/server/api/mutations/trace_annotations_mutations.py +127 -0
- phoenix/server/api/mutations/user_mutations.py +329 -0
- phoenix/server/api/openapi/__init__.py +0 -0
- phoenix/server/api/openapi/main.py +17 -0
- phoenix/server/api/openapi/schema.py +16 -0
- phoenix/server/api/queries.py +738 -0
- phoenix/server/api/routers/__init__.py +11 -0
- phoenix/server/api/routers/auth.py +284 -0
- phoenix/server/api/routers/embeddings.py +26 -0
- phoenix/server/api/routers/oauth2.py +488 -0
- phoenix/server/api/routers/v1/__init__.py +64 -0
- phoenix/server/api/routers/v1/datasets.py +1017 -0
- phoenix/server/api/routers/v1/evaluations.py +362 -0
- phoenix/server/api/routers/v1/experiment_evaluations.py +115 -0
- phoenix/server/api/routers/v1/experiment_runs.py +167 -0
- phoenix/server/api/routers/v1/experiments.py +308 -0
- phoenix/server/api/routers/v1/pydantic_compat.py +78 -0
- phoenix/server/api/routers/v1/spans.py +267 -0
- phoenix/server/api/routers/v1/traces.py +208 -0
- phoenix/server/api/routers/v1/utils.py +95 -0
- phoenix/server/api/schema.py +44 -247
- phoenix/server/api/subscriptions.py +597 -0
- phoenix/server/api/types/Annotation.py +21 -0
- phoenix/server/api/types/AnnotationSummary.py +55 -0
- phoenix/server/api/types/AnnotatorKind.py +16 -0
- phoenix/server/api/types/ApiKey.py +27 -0
- phoenix/server/api/types/AuthMethod.py +9 -0
- phoenix/server/api/types/ChatCompletionMessageRole.py +11 -0
- phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +46 -0
- phoenix/server/api/types/Cluster.py +25 -24
- phoenix/server/api/types/CreateDatasetPayload.py +8 -0
- phoenix/server/api/types/DataQualityMetric.py +31 -13
- phoenix/server/api/types/Dataset.py +288 -63
- phoenix/server/api/types/DatasetExample.py +85 -0
- phoenix/server/api/types/DatasetExampleRevision.py +34 -0
- phoenix/server/api/types/DatasetVersion.py +14 -0
- phoenix/server/api/types/Dimension.py +32 -31
- phoenix/server/api/types/DocumentEvaluationSummary.py +9 -8
- phoenix/server/api/types/EmbeddingDimension.py +56 -49
- phoenix/server/api/types/Evaluation.py +25 -31
- phoenix/server/api/types/EvaluationSummary.py +30 -50
- phoenix/server/api/types/Event.py +20 -20
- phoenix/server/api/types/ExampleRevisionInterface.py +14 -0
- phoenix/server/api/types/Experiment.py +152 -0
- phoenix/server/api/types/ExperimentAnnotationSummary.py +13 -0
- phoenix/server/api/types/ExperimentComparison.py +17 -0
- phoenix/server/api/types/ExperimentRun.py +119 -0
- phoenix/server/api/types/ExperimentRunAnnotation.py +56 -0
- phoenix/server/api/types/GenerativeModel.py +9 -0
- phoenix/server/api/types/GenerativeProvider.py +85 -0
- phoenix/server/api/types/Inferences.py +80 -0
- phoenix/server/api/types/InferencesRole.py +23 -0
- phoenix/server/api/types/LabelFraction.py +7 -0
- phoenix/server/api/types/MimeType.py +2 -2
- phoenix/server/api/types/Model.py +54 -54
- phoenix/server/api/types/PerformanceMetric.py +8 -5
- phoenix/server/api/types/Project.py +407 -142
- phoenix/server/api/types/ProjectSession.py +139 -0
- phoenix/server/api/types/Segments.py +4 -4
- phoenix/server/api/types/Span.py +221 -176
- phoenix/server/api/types/SpanAnnotation.py +43 -0
- phoenix/server/api/types/SpanIOValue.py +15 -0
- phoenix/server/api/types/SystemApiKey.py +9 -0
- phoenix/server/api/types/TemplateLanguage.py +10 -0
- phoenix/server/api/types/TimeSeries.py +19 -15
- phoenix/server/api/types/TokenUsage.py +11 -0
- phoenix/server/api/types/Trace.py +154 -0
- phoenix/server/api/types/TraceAnnotation.py +45 -0
- phoenix/server/api/types/UMAPPoints.py +7 -7
- phoenix/server/api/types/User.py +60 -0
- phoenix/server/api/types/UserApiKey.py +45 -0
- phoenix/server/api/types/UserRole.py +15 -0
- phoenix/server/api/types/node.py +13 -107
- phoenix/server/api/types/pagination.py +156 -57
- phoenix/server/api/utils.py +34 -0
- phoenix/server/app.py +864 -115
- phoenix/server/bearer_auth.py +163 -0
- phoenix/server/dml_event.py +136 -0
- phoenix/server/dml_event_handler.py +256 -0
- phoenix/server/email/__init__.py +0 -0
- phoenix/server/email/sender.py +97 -0
- phoenix/server/email/templates/__init__.py +0 -0
- phoenix/server/email/templates/password_reset.html +19 -0
- phoenix/server/email/types.py +11 -0
- phoenix/server/grpc_server.py +102 -0
- phoenix/server/jwt_store.py +505 -0
- phoenix/server/main.py +305 -116
- phoenix/server/oauth2.py +52 -0
- phoenix/server/openapi/__init__.py +0 -0
- phoenix/server/prometheus.py +111 -0
- phoenix/server/rate_limiters.py +188 -0
- phoenix/server/static/.vite/manifest.json +87 -0
- phoenix/server/static/assets/components-Cy9nwIvF.js +2125 -0
- phoenix/server/static/assets/index-BKvHIxkk.js +113 -0
- phoenix/server/static/assets/pages-CUi2xCVQ.js +4449 -0
- phoenix/server/static/assets/vendor-DvC8cT4X.js +894 -0
- phoenix/server/static/assets/vendor-DxkFTwjz.css +1 -0
- phoenix/server/static/assets/vendor-arizeai-Do1793cv.js +662 -0
- phoenix/server/static/assets/vendor-codemirror-BzwZPyJM.js +24 -0
- phoenix/server/static/assets/vendor-recharts-_Jb7JjhG.js +59 -0
- phoenix/server/static/assets/vendor-shiki-Cl9QBraO.js +5 -0
- phoenix/server/static/assets/vendor-three-DwGkEfCM.js +2998 -0
- phoenix/server/telemetry.py +68 -0
- phoenix/server/templates/index.html +82 -23
- phoenix/server/thread_server.py +3 -3
- phoenix/server/types.py +275 -0
- phoenix/services.py +27 -18
- phoenix/session/client.py +743 -68
- phoenix/session/data_extractor.py +31 -7
- phoenix/session/evaluation.py +3 -9
- phoenix/session/session.py +263 -219
- phoenix/settings.py +22 -0
- phoenix/trace/__init__.py +2 -22
- phoenix/trace/attributes.py +338 -0
- phoenix/trace/dsl/README.md +116 -0
- phoenix/trace/dsl/filter.py +663 -213
- phoenix/trace/dsl/helpers.py +73 -21
- phoenix/trace/dsl/query.py +574 -201
- phoenix/trace/exporter.py +24 -19
- phoenix/trace/fixtures.py +368 -32
- phoenix/trace/otel.py +71 -219
- phoenix/trace/projects.py +3 -2
- phoenix/trace/schemas.py +33 -11
- phoenix/trace/span_evaluations.py +21 -16
- phoenix/trace/span_json_decoder.py +6 -4
- phoenix/trace/span_json_encoder.py +2 -2
- phoenix/trace/trace_dataset.py +47 -32
- phoenix/trace/utils.py +21 -4
- phoenix/utilities/__init__.py +0 -26
- phoenix/utilities/client.py +132 -0
- phoenix/utilities/deprecation.py +31 -0
- phoenix/utilities/error_handling.py +3 -2
- phoenix/utilities/json.py +109 -0
- phoenix/utilities/logging.py +8 -0
- phoenix/utilities/project.py +2 -2
- phoenix/utilities/re.py +49 -0
- phoenix/utilities/span_store.py +0 -23
- phoenix/utilities/template_formatters.py +99 -0
- phoenix/version.py +1 -1
- arize_phoenix-3.16.0.dist-info/METADATA +0 -495
- arize_phoenix-3.16.0.dist-info/RECORD +0 -178
- phoenix/core/project.py +0 -617
- phoenix/core/traces.py +0 -100
- phoenix/experimental/evals/__init__.py +0 -73
- phoenix/experimental/evals/evaluators.py +0 -413
- phoenix/experimental/evals/functions/__init__.py +0 -4
- phoenix/experimental/evals/functions/classify.py +0 -453
- phoenix/experimental/evals/functions/executor.py +0 -353
- phoenix/experimental/evals/functions/generate.py +0 -138
- phoenix/experimental/evals/functions/processing.py +0 -76
- phoenix/experimental/evals/models/__init__.py +0 -14
- phoenix/experimental/evals/models/anthropic.py +0 -175
- phoenix/experimental/evals/models/base.py +0 -170
- phoenix/experimental/evals/models/bedrock.py +0 -221
- phoenix/experimental/evals/models/litellm.py +0 -134
- phoenix/experimental/evals/models/openai.py +0 -448
- phoenix/experimental/evals/models/rate_limiters.py +0 -246
- phoenix/experimental/evals/models/vertex.py +0 -173
- phoenix/experimental/evals/models/vertexai.py +0 -186
- phoenix/experimental/evals/retrievals.py +0 -96
- phoenix/experimental/evals/templates/__init__.py +0 -50
- phoenix/experimental/evals/templates/default_templates.py +0 -472
- phoenix/experimental/evals/templates/template.py +0 -195
- phoenix/experimental/evals/utils/__init__.py +0 -172
- phoenix/experimental/evals/utils/threads.py +0 -27
- phoenix/server/api/helpers.py +0 -11
- phoenix/server/api/routers/evaluation_handler.py +0 -109
- phoenix/server/api/routers/span_handler.py +0 -70
- phoenix/server/api/routers/trace_handler.py +0 -60
- phoenix/server/api/types/DatasetRole.py +0 -23
- phoenix/server/static/index.css +0 -6
- phoenix/server/static/index.js +0 -7447
- phoenix/storage/span_store/__init__.py +0 -23
- phoenix/storage/span_store/text_file.py +0 -85
- phoenix/trace/dsl/missing.py +0 -60
- phoenix/trace/langchain/__init__.py +0 -3
- phoenix/trace/langchain/instrumentor.py +0 -35
- phoenix/trace/llama_index/__init__.py +0 -3
- phoenix/trace/llama_index/callback.py +0 -102
- phoenix/trace/openai/__init__.py +0 -3
- phoenix/trace/openai/instrumentor.py +0 -30
- {arize_phoenix-3.16.0.dist-info → arize_phoenix-7.7.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-3.16.0.dist-info → arize_phoenix-7.7.0.dist-info}/licenses/LICENSE +0 -0
- /phoenix/{datasets → db/insertion}/__init__.py +0 -0
- /phoenix/{experimental → db/migrations}/__init__.py +0 -0
- /phoenix/{storage → db/migrations/data_migration_scripts}/__init__.py +0 -0
|
@@ -1,30 +1,83 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
from typing import Any,
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum, auto
|
|
3
|
+
from typing import Any, Optional, Protocol
|
|
4
4
|
|
|
5
|
-
import pandas as pd
|
|
6
5
|
import strawberry
|
|
7
|
-
from
|
|
6
|
+
from sqlalchemy import and_, desc, nulls_last
|
|
7
|
+
from sqlalchemy.orm import InstrumentedAttribute
|
|
8
|
+
from sqlalchemy.sql.expression import Select
|
|
8
9
|
from strawberry import UNSET
|
|
9
10
|
from typing_extensions import assert_never
|
|
10
11
|
|
|
11
12
|
import phoenix.trace.v1 as pb
|
|
12
|
-
from phoenix.
|
|
13
|
+
from phoenix.db import models
|
|
14
|
+
from phoenix.server.api.types.pagination import CursorSortColumnDataType
|
|
13
15
|
from phoenix.server.api.types.SortDir import SortDir
|
|
14
|
-
from phoenix.trace.schemas import
|
|
16
|
+
from phoenix.trace.schemas import SpanID
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
@strawberry.enum
|
|
18
20
|
class SpanColumn(Enum):
|
|
19
|
-
startTime =
|
|
20
|
-
endTime =
|
|
21
|
-
latencyMs =
|
|
22
|
-
tokenCountTotal =
|
|
23
|
-
tokenCountPrompt =
|
|
24
|
-
tokenCountCompletion =
|
|
25
|
-
cumulativeTokenCountTotal =
|
|
26
|
-
cumulativeTokenCountPrompt =
|
|
27
|
-
cumulativeTokenCountCompletion =
|
|
21
|
+
startTime = auto()
|
|
22
|
+
endTime = auto()
|
|
23
|
+
latencyMs = auto()
|
|
24
|
+
tokenCountTotal = auto()
|
|
25
|
+
tokenCountPrompt = auto()
|
|
26
|
+
tokenCountCompletion = auto()
|
|
27
|
+
cumulativeTokenCountTotal = auto()
|
|
28
|
+
cumulativeTokenCountPrompt = auto()
|
|
29
|
+
cumulativeTokenCountCompletion = auto()
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def column_name(self) -> str:
|
|
33
|
+
return "f{self.name}_span_sort_column"
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def orm_expression(self) -> Any:
|
|
37
|
+
expr: Any
|
|
38
|
+
if self is SpanColumn.startTime:
|
|
39
|
+
expr = models.Span.start_time
|
|
40
|
+
elif self is SpanColumn.endTime:
|
|
41
|
+
expr = models.Span.end_time
|
|
42
|
+
elif self is SpanColumn.latencyMs:
|
|
43
|
+
expr = models.Span.latency_ms
|
|
44
|
+
elif self is SpanColumn.tokenCountTotal:
|
|
45
|
+
expr = models.Span.llm_token_count_total
|
|
46
|
+
elif self is SpanColumn.tokenCountPrompt:
|
|
47
|
+
expr = models.Span.llm_token_count_prompt
|
|
48
|
+
elif self is SpanColumn.tokenCountCompletion:
|
|
49
|
+
expr = models.Span.llm_token_count_completion
|
|
50
|
+
elif self is SpanColumn.cumulativeTokenCountTotal:
|
|
51
|
+
expr = (
|
|
52
|
+
models.Span.cumulative_llm_token_count_prompt
|
|
53
|
+
+ models.Span.cumulative_llm_token_count_completion
|
|
54
|
+
)
|
|
55
|
+
elif self is SpanColumn.cumulativeTokenCountPrompt:
|
|
56
|
+
expr = models.Span.cumulative_llm_token_count_prompt
|
|
57
|
+
elif self is SpanColumn.cumulativeTokenCountCompletion:
|
|
58
|
+
expr = models.Span.cumulative_llm_token_count_completion
|
|
59
|
+
else:
|
|
60
|
+
assert_never(self)
|
|
61
|
+
return expr.label(self.column_name)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def data_type(self) -> CursorSortColumnDataType:
|
|
65
|
+
if (
|
|
66
|
+
self is SpanColumn.cumulativeTokenCountTotal
|
|
67
|
+
or self is SpanColumn.cumulativeTokenCountPrompt
|
|
68
|
+
or self is SpanColumn.cumulativeTokenCountCompletion
|
|
69
|
+
):
|
|
70
|
+
return CursorSortColumnDataType.INT
|
|
71
|
+
if (
|
|
72
|
+
self is SpanColumn.latencyMs
|
|
73
|
+
or self is SpanColumn.tokenCountTotal
|
|
74
|
+
or self is SpanColumn.tokenCountPrompt
|
|
75
|
+
or self is SpanColumn.tokenCountCompletion
|
|
76
|
+
):
|
|
77
|
+
return CursorSortColumnDataType.FLOAT
|
|
78
|
+
if self is SpanColumn.startTime or self is SpanColumn.endTime:
|
|
79
|
+
return CursorSortColumnDataType.DATETIME
|
|
80
|
+
assert_never(self)
|
|
28
81
|
|
|
29
82
|
|
|
30
83
|
@strawberry.enum
|
|
@@ -32,6 +85,29 @@ class EvalAttr(Enum):
|
|
|
32
85
|
score = "score"
|
|
33
86
|
label = "label"
|
|
34
87
|
|
|
88
|
+
@property
|
|
89
|
+
def column_name(self) -> str:
|
|
90
|
+
return f"{self.value}_eval_sort_column"
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def orm_expression(self) -> Any:
|
|
94
|
+
expr: InstrumentedAttribute[Any]
|
|
95
|
+
if self is EvalAttr.score:
|
|
96
|
+
expr = models.SpanAnnotation.score
|
|
97
|
+
elif self is EvalAttr.label:
|
|
98
|
+
expr = models.SpanAnnotation.label
|
|
99
|
+
else:
|
|
100
|
+
assert_never(self)
|
|
101
|
+
return expr.label(self.column_name)
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def data_type(self) -> CursorSortColumnDataType:
|
|
105
|
+
if self is EvalAttr.label:
|
|
106
|
+
return CursorSortColumnDataType.STRING
|
|
107
|
+
if self is EvalAttr.score:
|
|
108
|
+
return CursorSortColumnDataType.FLOAT
|
|
109
|
+
assert_never(self)
|
|
110
|
+
|
|
35
111
|
|
|
36
112
|
@strawberry.input
|
|
37
113
|
class EvalResultKey:
|
|
@@ -43,6 +119,15 @@ class SupportsGetSpanEvaluation(Protocol):
|
|
|
43
119
|
def get_span_evaluation(self, span_id: SpanID, name: str) -> Optional[pb.Evaluation]: ...
|
|
44
120
|
|
|
45
121
|
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class SpanSortConfig:
|
|
124
|
+
stmt: Select[Any]
|
|
125
|
+
orm_expression: Any
|
|
126
|
+
dir: SortDir
|
|
127
|
+
column_name: str
|
|
128
|
+
column_data_type: CursorSortColumnDataType
|
|
129
|
+
|
|
130
|
+
|
|
46
131
|
@strawberry.input(
|
|
47
132
|
description="The sort key and direction for span connections. Must "
|
|
48
133
|
"specify one and only one of either `col` or `evalResultKey`."
|
|
@@ -52,58 +137,38 @@ class SpanSort:
|
|
|
52
137
|
eval_result_key: Optional[EvalResultKey] = UNSET
|
|
53
138
|
dir: SortDir
|
|
54
139
|
|
|
55
|
-
def
|
|
56
|
-
self
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
eval_attr=self.eval_result_key.attr,
|
|
68
|
-
evals=evals,
|
|
140
|
+
def update_orm_expr(self, stmt: Select[Any]) -> SpanSortConfig:
|
|
141
|
+
if (col := self.col) and not self.eval_result_key:
|
|
142
|
+
expr = col.orm_expression
|
|
143
|
+
stmt = stmt.add_columns(expr)
|
|
144
|
+
if self.dir == SortDir.desc:
|
|
145
|
+
expr = desc(expr)
|
|
146
|
+
return SpanSortConfig(
|
|
147
|
+
stmt=stmt.order_by(nulls_last(expr)),
|
|
148
|
+
orm_expression=col.orm_expression,
|
|
149
|
+
dir=self.dir,
|
|
150
|
+
column_name=col.column_name,
|
|
151
|
+
column_data_type=col.data_type,
|
|
69
152
|
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
153
|
+
if (eval_result_key := self.eval_result_key) and not col:
|
|
154
|
+
eval_name = eval_result_key.name
|
|
155
|
+
eval_attr = eval_result_key.attr
|
|
156
|
+
expr = eval_result_key.attr.orm_expression
|
|
157
|
+
stmt = stmt.add_columns(expr)
|
|
158
|
+
if self.dir == SortDir.desc:
|
|
159
|
+
expr = desc(expr)
|
|
160
|
+
stmt = stmt.join(
|
|
161
|
+
models.SpanAnnotation,
|
|
162
|
+
onclause=and_(
|
|
163
|
+
models.SpanAnnotation.span_rowid == models.Span.id,
|
|
164
|
+
models.SpanAnnotation.name == eval_name,
|
|
165
|
+
),
|
|
166
|
+
).order_by(expr)
|
|
167
|
+
return SpanSortConfig(
|
|
168
|
+
stmt=stmt,
|
|
169
|
+
orm_expression=eval_result_key.attr.orm_expression,
|
|
170
|
+
dir=self.dir,
|
|
171
|
+
column_name=eval_attr.column_name,
|
|
172
|
+
column_data_type=eval_attr.data_type,
|
|
74
173
|
)
|
|
75
|
-
|
|
76
|
-
key=lambda series: series.apply(get_sort_key_value),
|
|
77
|
-
ascending=self.dir.value == SortDir.asc.value,
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def _get_column_value(span: WrappedSpan, span_column: SpanColumn) -> Any:
|
|
82
|
-
if span_column is SpanColumn.startTime:
|
|
83
|
-
return span.start_time
|
|
84
|
-
if span_column is SpanColumn.endTime:
|
|
85
|
-
return span.end_time
|
|
86
|
-
return span[span_column.value]
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _get_eval_result_value(
|
|
90
|
-
span: WrappedSpan,
|
|
91
|
-
eval_name: str,
|
|
92
|
-
eval_attr: EvalAttr,
|
|
93
|
-
evals: Optional[SupportsGetSpanEvaluation] = None,
|
|
94
|
-
) -> Any:
|
|
95
|
-
"""
|
|
96
|
-
Returns the evaluation result for the given span
|
|
97
|
-
"""
|
|
98
|
-
if evals is None:
|
|
99
|
-
return None
|
|
100
|
-
span_id = span.context.span_id
|
|
101
|
-
evaluation = evals.get_span_evaluation(span_id, eval_name)
|
|
102
|
-
if evaluation is None:
|
|
103
|
-
return None
|
|
104
|
-
result = evaluation.result
|
|
105
|
-
if eval_attr is EvalAttr.score:
|
|
106
|
-
return result.score.value if result.HasField("score") else None
|
|
107
|
-
if eval_attr is EvalAttr.label:
|
|
108
|
-
return result.label.value if result.HasField("label") else None
|
|
109
|
-
assert_never(eval_attr)
|
|
174
|
+
raise ValueError("Exactly one of `col` or `evalResultKey` must be specified on `SpanSort`.")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
import strawberry
|
|
4
|
+
|
|
5
|
+
from phoenix.server.api.types.SortDir import SortDir
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@strawberry.enum
|
|
9
|
+
class TraceAnnotationColumn(Enum):
|
|
10
|
+
createdAt = "created_at"
|
|
11
|
+
name = "name"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@strawberry.input(description="The sort key and direction for TraceAnnotation connections")
|
|
15
|
+
class TraceAnnotationSort:
|
|
16
|
+
col: TraceAnnotationColumn
|
|
17
|
+
dir: SortDir
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import strawberry
|
|
2
|
+
|
|
3
|
+
from phoenix.server.api.mutations.api_key_mutations import ApiKeyMutationMixin
|
|
4
|
+
from phoenix.server.api.mutations.chat_mutations import (
|
|
5
|
+
ChatCompletionMutationMixin,
|
|
6
|
+
)
|
|
7
|
+
from phoenix.server.api.mutations.dataset_mutations import DatasetMutationMixin
|
|
8
|
+
from phoenix.server.api.mutations.experiment_mutations import ExperimentMutationMixin
|
|
9
|
+
from phoenix.server.api.mutations.export_events_mutations import ExportEventsMutationMixin
|
|
10
|
+
from phoenix.server.api.mutations.project_mutations import ProjectMutationMixin
|
|
11
|
+
from phoenix.server.api.mutations.span_annotations_mutations import SpanAnnotationMutationMixin
|
|
12
|
+
from phoenix.server.api.mutations.trace_annotations_mutations import TraceAnnotationMutationMixin
|
|
13
|
+
from phoenix.server.api.mutations.user_mutations import UserMutationMixin
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@strawberry.type
|
|
17
|
+
class Mutation(
|
|
18
|
+
ApiKeyMutationMixin,
|
|
19
|
+
DatasetMutationMixin,
|
|
20
|
+
ExperimentMutationMixin,
|
|
21
|
+
ExportEventsMutationMixin,
|
|
22
|
+
ProjectMutationMixin,
|
|
23
|
+
SpanAnnotationMutationMixin,
|
|
24
|
+
TraceAnnotationMutationMixin,
|
|
25
|
+
UserMutationMixin,
|
|
26
|
+
ChatCompletionMutationMixin,
|
|
27
|
+
):
|
|
28
|
+
pass
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import strawberry
|
|
5
|
+
from sqlalchemy import select
|
|
6
|
+
from strawberry import UNSET
|
|
7
|
+
from strawberry.relay import GlobalID
|
|
8
|
+
from strawberry.types import Info
|
|
9
|
+
|
|
10
|
+
from phoenix.db import enums, models
|
|
11
|
+
from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly
|
|
12
|
+
from phoenix.server.api.context import Context
|
|
13
|
+
from phoenix.server.api.exceptions import Unauthorized
|
|
14
|
+
from phoenix.server.api.queries import Query
|
|
15
|
+
from phoenix.server.api.types.node import from_global_id_with_expected_type
|
|
16
|
+
from phoenix.server.api.types.SystemApiKey import SystemApiKey
|
|
17
|
+
from phoenix.server.api.types.UserApiKey import UserApiKey
|
|
18
|
+
from phoenix.server.bearer_auth import PhoenixUser
|
|
19
|
+
from phoenix.server.types import ApiKeyAttributes, ApiKeyClaims, ApiKeyId, UserId
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@strawberry.type
|
|
23
|
+
class CreateSystemApiKeyMutationPayload:
|
|
24
|
+
jwt: str
|
|
25
|
+
api_key: SystemApiKey
|
|
26
|
+
query: Query
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@strawberry.input
|
|
30
|
+
class CreateApiKeyInput:
|
|
31
|
+
name: str
|
|
32
|
+
description: Optional[str] = UNSET
|
|
33
|
+
expires_at: Optional[datetime] = UNSET
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@strawberry.type
|
|
37
|
+
class CreateUserApiKeyMutationPayload:
|
|
38
|
+
jwt: str
|
|
39
|
+
api_key: UserApiKey
|
|
40
|
+
query: Query
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@strawberry.input
|
|
44
|
+
class CreateUserApiKeyInput:
|
|
45
|
+
name: str
|
|
46
|
+
description: Optional[str] = UNSET
|
|
47
|
+
expires_at: Optional[datetime] = UNSET
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@strawberry.input
|
|
51
|
+
class DeleteApiKeyInput:
|
|
52
|
+
id: GlobalID
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@strawberry.type
|
|
56
|
+
class DeleteApiKeyMutationPayload:
|
|
57
|
+
apiKeyId: GlobalID
|
|
58
|
+
query: Query
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@strawberry.type
|
|
62
|
+
class ApiKeyMutationMixin:
|
|
63
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin, IsLocked]) # type: ignore
|
|
64
|
+
async def create_system_api_key(
|
|
65
|
+
self, info: Info[Context, None], input: CreateApiKeyInput
|
|
66
|
+
) -> CreateSystemApiKeyMutationPayload:
|
|
67
|
+
assert (token_store := info.context.token_store) is not None
|
|
68
|
+
user_role = enums.UserRole.SYSTEM
|
|
69
|
+
async with info.context.db() as session:
|
|
70
|
+
# Get the system user - note this could be pushed into a dataloader
|
|
71
|
+
system_user = await session.scalar(
|
|
72
|
+
select(models.User)
|
|
73
|
+
.join(models.UserRole) # Join User with UserRole
|
|
74
|
+
.where(models.UserRole.name == user_role.value) # Filter where role is SYSTEM
|
|
75
|
+
.order_by(models.User.id)
|
|
76
|
+
.limit(1)
|
|
77
|
+
)
|
|
78
|
+
if system_user is None:
|
|
79
|
+
raise ValueError("System user not found")
|
|
80
|
+
issued_at = datetime.now(timezone.utc)
|
|
81
|
+
claims = ApiKeyClaims(
|
|
82
|
+
subject=UserId(system_user.id),
|
|
83
|
+
issued_at=issued_at,
|
|
84
|
+
expiration_time=input.expires_at or None,
|
|
85
|
+
attributes=ApiKeyAttributes(
|
|
86
|
+
user_role=user_role,
|
|
87
|
+
name=input.name,
|
|
88
|
+
description=input.description,
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
token, token_id = await token_store.create_api_key(claims)
|
|
92
|
+
return CreateSystemApiKeyMutationPayload(
|
|
93
|
+
jwt=token,
|
|
94
|
+
api_key=SystemApiKey(
|
|
95
|
+
id_attr=int(token_id),
|
|
96
|
+
name=input.name,
|
|
97
|
+
description=input.description or None,
|
|
98
|
+
created_at=issued_at,
|
|
99
|
+
expires_at=input.expires_at or None,
|
|
100
|
+
),
|
|
101
|
+
query=Query(),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
|
|
105
|
+
async def create_user_api_key(
|
|
106
|
+
self, info: Info[Context, None], input: CreateUserApiKeyInput
|
|
107
|
+
) -> CreateUserApiKeyMutationPayload:
|
|
108
|
+
assert (token_store := info.context.token_store) is not None
|
|
109
|
+
try:
|
|
110
|
+
user = info.context.request.user # type: ignore
|
|
111
|
+
assert isinstance(user, PhoenixUser)
|
|
112
|
+
except AttributeError:
|
|
113
|
+
raise ValueError("User not found")
|
|
114
|
+
issued_at = datetime.now(timezone.utc)
|
|
115
|
+
claims = ApiKeyClaims(
|
|
116
|
+
subject=user.identity,
|
|
117
|
+
issued_at=issued_at,
|
|
118
|
+
expiration_time=input.expires_at or None,
|
|
119
|
+
attributes=ApiKeyAttributes(
|
|
120
|
+
user_role=enums.UserRole.MEMBER,
|
|
121
|
+
name=input.name,
|
|
122
|
+
description=input.description,
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
token, token_id = await token_store.create_api_key(claims)
|
|
126
|
+
return CreateUserApiKeyMutationPayload(
|
|
127
|
+
jwt=token,
|
|
128
|
+
api_key=UserApiKey(
|
|
129
|
+
id_attr=int(token_id),
|
|
130
|
+
name=input.name,
|
|
131
|
+
description=input.description or None,
|
|
132
|
+
created_at=issued_at,
|
|
133
|
+
expires_at=input.expires_at or None,
|
|
134
|
+
user_id=int(user.identity),
|
|
135
|
+
),
|
|
136
|
+
query=Query(),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin]) # type: ignore
|
|
140
|
+
async def delete_system_api_key(
|
|
141
|
+
self, info: Info[Context, None], input: DeleteApiKeyInput
|
|
142
|
+
) -> DeleteApiKeyMutationPayload:
|
|
143
|
+
assert (token_store := info.context.token_store) is not None
|
|
144
|
+
api_key_id = from_global_id_with_expected_type(
|
|
145
|
+
input.id, expected_type_name=SystemApiKey.__name__
|
|
146
|
+
)
|
|
147
|
+
await token_store.revoke(ApiKeyId(api_key_id))
|
|
148
|
+
return DeleteApiKeyMutationPayload(apiKeyId=input.id, query=Query())
|
|
149
|
+
|
|
150
|
+
@strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
|
|
151
|
+
async def delete_user_api_key(
|
|
152
|
+
self, info: Info[Context, None], input: DeleteApiKeyInput
|
|
153
|
+
) -> DeleteApiKeyMutationPayload:
|
|
154
|
+
assert (token_store := info.context.token_store) is not None
|
|
155
|
+
api_key_id = from_global_id_with_expected_type(
|
|
156
|
+
input.id, expected_type_name=UserApiKey.__name__
|
|
157
|
+
)
|
|
158
|
+
async with info.context.db() as session:
|
|
159
|
+
api_key = await session.scalar(
|
|
160
|
+
select(models.ApiKey).where(models.ApiKey.id == api_key_id)
|
|
161
|
+
)
|
|
162
|
+
if api_key is None:
|
|
163
|
+
raise ValueError(f"API key with id {input.id} not found")
|
|
164
|
+
if int((user := info.context.user).identity) != api_key.user_id and not user.is_admin:
|
|
165
|
+
raise Unauthorized("User not authorized to delete")
|
|
166
|
+
await token_store.revoke(ApiKeyId(api_key_id))
|
|
167
|
+
return DeleteApiKeyMutationPayload(apiKeyId=input.id, query=Query())
|