arize-phoenix 10.0.4__py3-none-any.whl → 12.28.1__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.
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +124 -72
- arize_phoenix-12.28.1.dist-info/RECORD +499 -0
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/IP_NOTICE +1 -1
- phoenix/__generated__/__init__.py +0 -0
- phoenix/__generated__/classification_evaluator_configs/__init__.py +20 -0
- phoenix/__generated__/classification_evaluator_configs/_document_relevance_classification_evaluator_config.py +17 -0
- phoenix/__generated__/classification_evaluator_configs/_hallucination_classification_evaluator_config.py +17 -0
- phoenix/__generated__/classification_evaluator_configs/_models.py +18 -0
- phoenix/__generated__/classification_evaluator_configs/_tool_selection_classification_evaluator_config.py +17 -0
- phoenix/__init__.py +5 -4
- phoenix/auth.py +39 -2
- phoenix/config.py +1763 -91
- phoenix/datetime_utils.py +120 -2
- phoenix/db/README.md +595 -25
- phoenix/db/bulk_inserter.py +145 -103
- phoenix/db/engines.py +140 -33
- phoenix/db/enums.py +3 -12
- phoenix/db/facilitator.py +302 -35
- phoenix/db/helpers.py +1000 -65
- phoenix/db/iam_auth.py +64 -0
- phoenix/db/insertion/dataset.py +135 -2
- phoenix/db/insertion/document_annotation.py +9 -6
- phoenix/db/insertion/evaluation.py +2 -3
- phoenix/db/insertion/helpers.py +17 -2
- phoenix/db/insertion/session_annotation.py +176 -0
- phoenix/db/insertion/span.py +15 -11
- phoenix/db/insertion/span_annotation.py +3 -4
- phoenix/db/insertion/trace_annotation.py +3 -4
- phoenix/db/insertion/types.py +50 -20
- phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
- phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
- phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
- phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
- phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
- phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
- phoenix/db/migrations/versions/a20694b15f82_cost.py +196 -0
- phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
- phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
- phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
- phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
- phoenix/db/models.py +669 -56
- phoenix/db/pg_config.py +10 -0
- phoenix/db/types/model_provider.py +4 -0
- phoenix/db/types/token_price_customization.py +29 -0
- phoenix/db/types/trace_retention.py +23 -15
- phoenix/experiments/evaluators/utils.py +3 -3
- phoenix/experiments/functions.py +160 -52
- phoenix/experiments/tracing.py +2 -2
- phoenix/experiments/types.py +1 -1
- phoenix/inferences/inferences.py +1 -2
- phoenix/server/api/auth.py +38 -7
- phoenix/server/api/auth_messages.py +46 -0
- phoenix/server/api/context.py +100 -4
- phoenix/server/api/dataloaders/__init__.py +79 -5
- phoenix/server/api/dataloaders/annotation_configs_by_project.py +31 -0
- phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
- phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
- phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
- phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
- phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
- phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
- phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
- phoenix/server/api/dataloaders/dataset_labels.py +36 -0
- phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
- phoenix/server/api/dataloaders/document_evaluations.py +6 -9
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
- phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
- phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
- phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
- phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
- phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
- phoenix/server/api/dataloaders/last_used_times_by_generative_model_id.py +35 -0
- phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
- phoenix/server/api/dataloaders/record_counts.py +37 -10
- phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
- phoenix/server/api/dataloaders/span_cost_by_span.py +24 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_generative_model.py +56 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_project_session.py +57 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_span.py +43 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_trace.py +56 -0
- phoenix/server/api/dataloaders/span_cost_details_by_span_cost.py +27 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment.py +57 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment_run.py +58 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_generative_model.py +55 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_project.py +152 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_project_session.py +56 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_trace.py +55 -0
- phoenix/server/api/dataloaders/span_costs.py +29 -0
- phoenix/server/api/dataloaders/table_fields.py +2 -2
- phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
- phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
- phoenix/server/api/dataloaders/types.py +29 -0
- phoenix/server/api/exceptions.py +11 -1
- phoenix/server/api/helpers/dataset_helpers.py +5 -1
- phoenix/server/api/helpers/playground_clients.py +1243 -292
- phoenix/server/api/helpers/playground_registry.py +2 -2
- phoenix/server/api/helpers/playground_spans.py +8 -4
- phoenix/server/api/helpers/playground_users.py +26 -0
- phoenix/server/api/helpers/prompts/conversions/aws.py +83 -0
- phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
- phoenix/server/api/helpers/prompts/models.py +205 -22
- phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
- phoenix/server/api/input_types/ChatCompletionInput.py +6 -2
- phoenix/server/api/input_types/CreateProjectInput.py +27 -0
- phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
- phoenix/server/api/input_types/DatasetFilter.py +17 -0
- phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
- phoenix/server/api/input_types/GenerativeCredentialInput.py +9 -0
- phoenix/server/api/input_types/GenerativeModelInput.py +5 -0
- phoenix/server/api/input_types/ProjectSessionSort.py +161 -1
- phoenix/server/api/input_types/PromptFilter.py +14 -0
- phoenix/server/api/input_types/PromptVersionInput.py +52 -1
- phoenix/server/api/input_types/SpanSort.py +44 -7
- phoenix/server/api/input_types/TimeBinConfig.py +23 -0
- phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
- phoenix/server/api/input_types/UserRoleInput.py +1 -0
- phoenix/server/api/mutations/__init__.py +10 -0
- phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
- phoenix/server/api/mutations/api_key_mutations.py +19 -23
- phoenix/server/api/mutations/chat_mutations.py +154 -47
- phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
- phoenix/server/api/mutations/dataset_mutations.py +21 -16
- phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
- phoenix/server/api/mutations/experiment_mutations.py +2 -2
- phoenix/server/api/mutations/export_events_mutations.py +3 -3
- phoenix/server/api/mutations/model_mutations.py +210 -0
- phoenix/server/api/mutations/project_mutations.py +49 -10
- phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
- phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
- phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
- phoenix/server/api/mutations/prompt_mutations.py +65 -129
- phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
- phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
- phoenix/server/api/mutations/trace_annotations_mutations.py +14 -10
- phoenix/server/api/mutations/trace_mutations.py +47 -3
- phoenix/server/api/mutations/user_mutations.py +66 -41
- phoenix/server/api/queries.py +768 -293
- phoenix/server/api/routers/__init__.py +2 -2
- phoenix/server/api/routers/auth.py +154 -88
- phoenix/server/api/routers/ldap.py +229 -0
- phoenix/server/api/routers/oauth2.py +369 -106
- phoenix/server/api/routers/v1/__init__.py +24 -4
- phoenix/server/api/routers/v1/annotation_configs.py +23 -31
- phoenix/server/api/routers/v1/annotations.py +481 -17
- phoenix/server/api/routers/v1/datasets.py +395 -81
- phoenix/server/api/routers/v1/documents.py +142 -0
- phoenix/server/api/routers/v1/evaluations.py +24 -31
- phoenix/server/api/routers/v1/experiment_evaluations.py +19 -8
- phoenix/server/api/routers/v1/experiment_runs.py +337 -59
- phoenix/server/api/routers/v1/experiments.py +479 -48
- phoenix/server/api/routers/v1/models.py +7 -0
- phoenix/server/api/routers/v1/projects.py +18 -49
- phoenix/server/api/routers/v1/prompts.py +54 -40
- phoenix/server/api/routers/v1/sessions.py +108 -0
- phoenix/server/api/routers/v1/spans.py +1091 -81
- phoenix/server/api/routers/v1/traces.py +132 -78
- phoenix/server/api/routers/v1/users.py +389 -0
- phoenix/server/api/routers/v1/utils.py +3 -7
- phoenix/server/api/subscriptions.py +305 -88
- phoenix/server/api/types/Annotation.py +90 -23
- phoenix/server/api/types/ApiKey.py +13 -17
- phoenix/server/api/types/AuthMethod.py +1 -0
- phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
- phoenix/server/api/types/CostBreakdown.py +12 -0
- phoenix/server/api/types/Dataset.py +226 -72
- phoenix/server/api/types/DatasetExample.py +88 -18
- phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
- phoenix/server/api/types/DatasetLabel.py +57 -0
- phoenix/server/api/types/DatasetSplit.py +98 -0
- phoenix/server/api/types/DatasetVersion.py +49 -4
- phoenix/server/api/types/DocumentAnnotation.py +212 -0
- phoenix/server/api/types/Experiment.py +264 -59
- phoenix/server/api/types/ExperimentComparison.py +5 -10
- phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
- phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
- phoenix/server/api/types/ExperimentRun.py +169 -65
- phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
- phoenix/server/api/types/GenerativeModel.py +245 -3
- phoenix/server/api/types/GenerativeProvider.py +70 -11
- phoenix/server/api/types/{Model.py → InferenceModel.py} +1 -1
- phoenix/server/api/types/ModelInterface.py +16 -0
- phoenix/server/api/types/PlaygroundModel.py +20 -0
- phoenix/server/api/types/Project.py +1278 -216
- phoenix/server/api/types/ProjectSession.py +188 -28
- phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
- phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
- phoenix/server/api/types/Prompt.py +119 -39
- phoenix/server/api/types/PromptLabel.py +42 -25
- phoenix/server/api/types/PromptVersion.py +11 -8
- phoenix/server/api/types/PromptVersionTag.py +65 -25
- phoenix/server/api/types/ServerStatus.py +6 -0
- phoenix/server/api/types/Span.py +167 -123
- phoenix/server/api/types/SpanAnnotation.py +189 -42
- phoenix/server/api/types/SpanCostDetailSummaryEntry.py +10 -0
- phoenix/server/api/types/SpanCostSummary.py +10 -0
- phoenix/server/api/types/SystemApiKey.py +65 -1
- phoenix/server/api/types/TokenPrice.py +16 -0
- phoenix/server/api/types/TokenUsage.py +3 -3
- phoenix/server/api/types/Trace.py +223 -51
- phoenix/server/api/types/TraceAnnotation.py +149 -50
- phoenix/server/api/types/User.py +137 -32
- phoenix/server/api/types/UserApiKey.py +73 -26
- phoenix/server/api/types/node.py +10 -0
- phoenix/server/api/types/pagination.py +11 -2
- phoenix/server/app.py +290 -45
- phoenix/server/authorization.py +38 -3
- phoenix/server/bearer_auth.py +34 -24
- phoenix/server/cost_tracking/cost_details_calculator.py +196 -0
- phoenix/server/cost_tracking/cost_model_lookup.py +179 -0
- phoenix/server/cost_tracking/helpers.py +68 -0
- phoenix/server/cost_tracking/model_cost_manifest.json +3657 -830
- phoenix/server/cost_tracking/regex_specificity.py +397 -0
- phoenix/server/cost_tracking/token_cost_calculator.py +57 -0
- phoenix/server/daemons/__init__.py +0 -0
- phoenix/server/daemons/db_disk_usage_monitor.py +214 -0
- phoenix/server/daemons/generative_model_store.py +103 -0
- phoenix/server/daemons/span_cost_calculator.py +99 -0
- phoenix/server/dml_event.py +17 -0
- phoenix/server/dml_event_handler.py +5 -0
- phoenix/server/email/sender.py +56 -3
- phoenix/server/email/templates/db_disk_usage_notification.html +19 -0
- phoenix/server/email/types.py +11 -0
- phoenix/server/experiments/__init__.py +0 -0
- phoenix/server/experiments/utils.py +14 -0
- phoenix/server/grpc_server.py +11 -11
- phoenix/server/jwt_store.py +17 -15
- phoenix/server/ldap.py +1449 -0
- phoenix/server/main.py +26 -10
- phoenix/server/oauth2.py +330 -12
- phoenix/server/prometheus.py +66 -6
- phoenix/server/rate_limiters.py +4 -9
- phoenix/server/retention.py +33 -20
- phoenix/server/session_filters.py +49 -0
- phoenix/server/static/.vite/manifest.json +55 -51
- phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
- phoenix/server/static/assets/{index-E0M82BdE.js → index-CTQoemZv.js} +140 -56
- phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
- phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
- phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
- phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
- phoenix/server/static/assets/vendor-recharts-V9cwpXsm.js +37 -0
- phoenix/server/static/assets/vendor-shiki-Do--csgv.js +5 -0
- phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
- phoenix/server/templates/index.html +40 -6
- phoenix/server/thread_server.py +1 -2
- phoenix/server/types.py +14 -4
- phoenix/server/utils.py +74 -0
- phoenix/session/client.py +56 -3
- phoenix/session/data_extractor.py +5 -0
- phoenix/session/evaluation.py +14 -5
- phoenix/session/session.py +45 -9
- phoenix/settings.py +5 -0
- phoenix/trace/attributes.py +80 -13
- phoenix/trace/dsl/helpers.py +90 -1
- phoenix/trace/dsl/query.py +8 -6
- phoenix/trace/projects.py +5 -0
- phoenix/utilities/template_formatters.py +1 -1
- phoenix/version.py +1 -1
- arize_phoenix-10.0.4.dist-info/RECORD +0 -405
- phoenix/server/api/types/Evaluation.py +0 -39
- phoenix/server/cost_tracking/cost_lookup.py +0 -255
- phoenix/server/static/assets/components-DULKeDfL.js +0 -4365
- phoenix/server/static/assets/pages-Cl0A-0U2.js +0 -7430
- phoenix/server/static/assets/vendor-WIZid84E.css +0 -1
- phoenix/server/static/assets/vendor-arizeai-Dy-0mSNw.js +0 -649
- phoenix/server/static/assets/vendor-codemirror-DBtifKNr.js +0 -33
- phoenix/server/static/assets/vendor-oB4u9zuV.js +0 -905
- phoenix/server/static/assets/vendor-recharts-D-T4KPz2.js +0 -59
- phoenix/server/static/assets/vendor-shiki-BMn4O_9F.js +0 -5
- phoenix/server/static/assets/vendor-three-C5WAXd5r.js +0 -2998
- phoenix/utilities/deprecation.py +0 -31
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,8 +2,8 @@ from datetime import datetime
|
|
|
2
2
|
from typing import TYPE_CHECKING, Annotated, Optional
|
|
3
3
|
|
|
4
4
|
import strawberry
|
|
5
|
-
from sqlalchemy import select
|
|
6
|
-
from sqlalchemy.
|
|
5
|
+
from sqlalchemy import func, select
|
|
6
|
+
from sqlalchemy.sql.functions import coalesce
|
|
7
7
|
from strawberry import UNSET
|
|
8
8
|
from strawberry.relay import Connection, GlobalID, Node, NodeID
|
|
9
9
|
from strawberry.scalars import JSON
|
|
@@ -11,30 +11,112 @@ from strawberry.types import Info
|
|
|
11
11
|
|
|
12
12
|
from phoenix.db import models
|
|
13
13
|
from phoenix.server.api.context import Context
|
|
14
|
-
from phoenix.server.api.types.
|
|
15
|
-
|
|
16
|
-
to_gql_experiment_run_annotation,
|
|
17
|
-
)
|
|
14
|
+
from phoenix.server.api.types.CostBreakdown import CostBreakdown
|
|
15
|
+
from phoenix.server.api.types.ExperimentRunAnnotation import ExperimentRunAnnotation
|
|
18
16
|
from phoenix.server.api.types.pagination import (
|
|
19
17
|
ConnectionArgs,
|
|
20
18
|
CursorString,
|
|
21
19
|
connection_from_list,
|
|
22
20
|
)
|
|
21
|
+
from phoenix.server.api.types.SpanCostDetailSummaryEntry import SpanCostDetailSummaryEntry
|
|
22
|
+
from phoenix.server.api.types.SpanCostSummary import SpanCostSummary
|
|
23
23
|
from phoenix.server.api.types.Trace import Trace
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
|
-
from
|
|
26
|
+
from .DatasetExample import DatasetExample
|
|
27
|
+
from .Trace import Trace
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
@strawberry.type
|
|
30
31
|
class ExperimentRun(Node):
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
id: NodeID[int]
|
|
33
|
+
db_record: strawberry.Private[Optional[models.ExperimentRun]] = None
|
|
34
|
+
|
|
35
|
+
def __post_init__(self) -> None:
|
|
36
|
+
if self.db_record and self.id != self.db_record.id:
|
|
37
|
+
raise ValueError("ExperimentRun ID mismatch")
|
|
38
|
+
|
|
39
|
+
@strawberry.field
|
|
40
|
+
async def experiment_id(self, info: Info[Context, None]) -> GlobalID:
|
|
41
|
+
from .Experiment import Experiment
|
|
42
|
+
|
|
43
|
+
if self.db_record:
|
|
44
|
+
experiment_id = self.db_record.experiment_id
|
|
45
|
+
else:
|
|
46
|
+
experiment_id = await info.context.data_loaders.experiment_run_fields.load(
|
|
47
|
+
(self.id, models.ExperimentRun.experiment_id),
|
|
48
|
+
)
|
|
49
|
+
return GlobalID(Experiment.__name__, str(experiment_id))
|
|
50
|
+
|
|
51
|
+
@strawberry.field
|
|
52
|
+
async def repetition_number(self, info: Info[Context, None]) -> int:
|
|
53
|
+
if self.db_record:
|
|
54
|
+
val = self.db_record.repetition_number
|
|
55
|
+
else:
|
|
56
|
+
val = await info.context.data_loaders.experiment_run_fields.load(
|
|
57
|
+
(self.id, models.ExperimentRun.repetition_number),
|
|
58
|
+
)
|
|
59
|
+
return val
|
|
60
|
+
|
|
61
|
+
@strawberry.field
|
|
62
|
+
async def trace_id(self, info: Info[Context, None]) -> Optional[str]:
|
|
63
|
+
if self.db_record:
|
|
64
|
+
val = self.db_record.trace_id
|
|
65
|
+
else:
|
|
66
|
+
val = await info.context.data_loaders.experiment_run_fields.load(
|
|
67
|
+
(self.id, models.ExperimentRun.trace_id),
|
|
68
|
+
)
|
|
69
|
+
return val
|
|
70
|
+
|
|
71
|
+
@strawberry.field
|
|
72
|
+
async def output(self, info: Info[Context, None]) -> Optional[JSON]:
|
|
73
|
+
if self.db_record:
|
|
74
|
+
output_dict = self.db_record.output
|
|
75
|
+
else:
|
|
76
|
+
output_dict = await info.context.data_loaders.experiment_run_fields.load(
|
|
77
|
+
(self.id, models.ExperimentRun.output),
|
|
78
|
+
)
|
|
79
|
+
return output_dict.get("task_output") if output_dict else None
|
|
80
|
+
|
|
81
|
+
@strawberry.field
|
|
82
|
+
async def start_time(self, info: Info[Context, None]) -> datetime:
|
|
83
|
+
if self.db_record:
|
|
84
|
+
val = self.db_record.start_time
|
|
85
|
+
else:
|
|
86
|
+
val = await info.context.data_loaders.experiment_run_fields.load(
|
|
87
|
+
(self.id, models.ExperimentRun.start_time),
|
|
88
|
+
)
|
|
89
|
+
return val
|
|
90
|
+
|
|
91
|
+
@strawberry.field
|
|
92
|
+
async def end_time(self, info: Info[Context, None]) -> datetime:
|
|
93
|
+
if self.db_record:
|
|
94
|
+
val = self.db_record.end_time
|
|
95
|
+
else:
|
|
96
|
+
val = await info.context.data_loaders.experiment_run_fields.load(
|
|
97
|
+
(self.id, models.ExperimentRun.end_time),
|
|
98
|
+
)
|
|
99
|
+
return val
|
|
100
|
+
|
|
101
|
+
@strawberry.field
|
|
102
|
+
async def error(self, info: Info[Context, None]) -> Optional[str]:
|
|
103
|
+
if self.db_record:
|
|
104
|
+
val = self.db_record.error
|
|
105
|
+
else:
|
|
106
|
+
val = await info.context.data_loaders.experiment_run_fields.load(
|
|
107
|
+
(self.id, models.ExperimentRun.error),
|
|
108
|
+
)
|
|
109
|
+
return val
|
|
110
|
+
|
|
111
|
+
@strawberry.field
|
|
112
|
+
async def latency_ms(self, info: Info[Context, None]) -> float:
|
|
113
|
+
if self.db_record:
|
|
114
|
+
val = self.db_record.latency_ms
|
|
115
|
+
else:
|
|
116
|
+
val = await info.context.data_loaders.experiment_run_fields.load(
|
|
117
|
+
(self.id, models.ExperimentRun.latency_ms),
|
|
118
|
+
)
|
|
119
|
+
return val
|
|
38
120
|
|
|
39
121
|
@strawberry.field
|
|
40
122
|
async def annotations(
|
|
@@ -51,69 +133,91 @@ class ExperimentRun(Node):
|
|
|
51
133
|
last=last,
|
|
52
134
|
before=before if isinstance(before, CursorString) else None,
|
|
53
135
|
)
|
|
54
|
-
|
|
55
|
-
annotations = await info.context.data_loaders.experiment_run_annotations.load(run_id)
|
|
136
|
+
annotations = await info.context.data_loaders.experiment_run_annotations.load(self.id)
|
|
56
137
|
return connection_from_list(
|
|
57
|
-
[
|
|
138
|
+
[
|
|
139
|
+
ExperimentRunAnnotation(id=annotation.id, db_record=annotation)
|
|
140
|
+
for annotation in annotations
|
|
141
|
+
],
|
|
142
|
+
args,
|
|
58
143
|
)
|
|
59
144
|
|
|
60
145
|
@strawberry.field
|
|
61
|
-
async def trace(
|
|
62
|
-
|
|
146
|
+
async def trace(
|
|
147
|
+
self, info: Info[Context, None]
|
|
148
|
+
) -> Optional[Annotated["Trace", strawberry.lazy(".Trace")]]:
|
|
149
|
+
if self.db_record:
|
|
150
|
+
trace_id = self.db_record.trace_id
|
|
151
|
+
else:
|
|
152
|
+
trace_id = await info.context.data_loaders.experiment_run_fields.load(
|
|
153
|
+
(self.id, models.ExperimentRun.trace_id),
|
|
154
|
+
)
|
|
155
|
+
if not trace_id:
|
|
63
156
|
return None
|
|
64
|
-
|
|
65
|
-
if (trace := await
|
|
157
|
+
loader = info.context.data_loaders.trace_by_trace_ids
|
|
158
|
+
if (trace := await loader.load(trace_id)) is None:
|
|
66
159
|
return None
|
|
67
|
-
|
|
160
|
+
from .Trace import Trace
|
|
161
|
+
|
|
162
|
+
return Trace(id=trace.id, db_record=trace)
|
|
68
163
|
|
|
69
164
|
@strawberry.field
|
|
70
165
|
async def example(
|
|
71
|
-
self, info: Info
|
|
166
|
+
self, info: Info[Context, None]
|
|
72
167
|
) -> Annotated[
|
|
73
|
-
"DatasetExample", strawberry.lazy("
|
|
168
|
+
"DatasetExample", strawberry.lazy(".DatasetExample")
|
|
74
169
|
]: # use lazy types to avoid circular import: https://strawberry.rocks/docs/types/lazy
|
|
75
|
-
from
|
|
170
|
+
from .DatasetExample import DatasetExample
|
|
76
171
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
created_at=example.created_at,
|
|
98
|
-
version_id=version_id,
|
|
172
|
+
loader = info.context.data_loaders.dataset_examples_and_versions_by_experiment_run
|
|
173
|
+
(example, version_id) = await loader.load(self.id)
|
|
174
|
+
return DatasetExample(id=example.id, db_record=example, version_id=version_id)
|
|
175
|
+
|
|
176
|
+
@strawberry.field
|
|
177
|
+
async def cost_summary(self, info: Info[Context, None]) -> SpanCostSummary:
|
|
178
|
+
summary = await info.context.data_loaders.span_cost_summary_by_experiment_run.load(self.id)
|
|
179
|
+
return SpanCostSummary(
|
|
180
|
+
prompt=CostBreakdown(
|
|
181
|
+
tokens=summary.prompt.tokens,
|
|
182
|
+
cost=summary.prompt.cost,
|
|
183
|
+
),
|
|
184
|
+
completion=CostBreakdown(
|
|
185
|
+
tokens=summary.completion.tokens,
|
|
186
|
+
cost=summary.completion.cost,
|
|
187
|
+
),
|
|
188
|
+
total=CostBreakdown(
|
|
189
|
+
tokens=summary.total.tokens,
|
|
190
|
+
cost=summary.total.cost,
|
|
191
|
+
),
|
|
99
192
|
)
|
|
100
193
|
|
|
194
|
+
@strawberry.field
|
|
195
|
+
async def cost_detail_summary_entries(
|
|
196
|
+
self, info: Info[Context, None]
|
|
197
|
+
) -> list[SpanCostDetailSummaryEntry]:
|
|
198
|
+
stmt = (
|
|
199
|
+
select(
|
|
200
|
+
models.SpanCostDetail.token_type,
|
|
201
|
+
models.SpanCostDetail.is_prompt,
|
|
202
|
+
coalesce(func.sum(models.SpanCostDetail.cost), 0).label("cost"),
|
|
203
|
+
coalesce(func.sum(models.SpanCostDetail.tokens), 0).label("tokens"),
|
|
204
|
+
)
|
|
205
|
+
.select_from(models.SpanCostDetail)
|
|
206
|
+
.join(models.SpanCost, models.SpanCostDetail.span_cost_id == models.SpanCost.id)
|
|
207
|
+
.join(models.Span, models.SpanCost.span_rowid == models.Span.id)
|
|
208
|
+
.join(models.Trace, models.Span.trace_rowid == models.Trace.id)
|
|
209
|
+
.join(models.ExperimentRun, models.ExperimentRun.trace_id == models.Trace.trace_id)
|
|
210
|
+
.where(models.ExperimentRun.id == self.id)
|
|
211
|
+
.group_by(models.SpanCostDetail.token_type, models.SpanCostDetail.is_prompt)
|
|
212
|
+
)
|
|
101
213
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
trace_id=trace_id
|
|
113
|
-
if (trace := run.trace) and (trace_id := trace.trace_id) is not None
|
|
114
|
-
else None,
|
|
115
|
-
output=run.output.get("task_output"),
|
|
116
|
-
start_time=run.start_time,
|
|
117
|
-
end_time=run.end_time,
|
|
118
|
-
error=run.error,
|
|
119
|
-
)
|
|
214
|
+
async with info.context.db() as session:
|
|
215
|
+
data = await session.stream(stmt)
|
|
216
|
+
return [
|
|
217
|
+
SpanCostDetailSummaryEntry(
|
|
218
|
+
token_type=token_type,
|
|
219
|
+
is_prompt=is_prompt,
|
|
220
|
+
value=CostBreakdown(tokens=tokens, cost=cost),
|
|
221
|
+
)
|
|
222
|
+
async for token_type, is_prompt, cost, tokens in data
|
|
223
|
+
]
|
|
@@ -1,56 +1,175 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from math import isfinite
|
|
2
3
|
from typing import Optional
|
|
3
4
|
|
|
4
5
|
import strawberry
|
|
5
6
|
from strawberry import Info
|
|
6
|
-
from strawberry.relay import Node, NodeID
|
|
7
|
+
from strawberry.relay import GlobalID, Node, NodeID
|
|
7
8
|
from strawberry.scalars import JSON
|
|
8
9
|
|
|
9
10
|
from phoenix.db import models
|
|
11
|
+
from phoenix.server.api.context import Context
|
|
10
12
|
from phoenix.server.api.types.AnnotatorKind import ExperimentRunAnnotatorKind
|
|
11
13
|
from phoenix.server.api.types.Trace import Trace
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
@strawberry.type
|
|
15
17
|
class ExperimentRunAnnotation(Node):
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
id: NodeID[int]
|
|
19
|
+
db_record: strawberry.Private[Optional[models.ExperimentRunAnnotation]] = None
|
|
20
|
+
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
if self.db_record and self.id != self.db_record.id:
|
|
23
|
+
raise ValueError("ExperimentRunAnnotation ID mismatch")
|
|
24
|
+
|
|
25
|
+
@strawberry.field(description="Name of the annotation, e.g. 'helpfulness' or 'relevance'.") # type: ignore
|
|
26
|
+
async def name(
|
|
27
|
+
self,
|
|
28
|
+
info: Info[Context, None],
|
|
29
|
+
) -> str:
|
|
30
|
+
if self.db_record:
|
|
31
|
+
val = self.db_record.name
|
|
32
|
+
else:
|
|
33
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
34
|
+
(self.id, models.ExperimentRunAnnotation.name),
|
|
35
|
+
)
|
|
36
|
+
return val
|
|
37
|
+
|
|
38
|
+
@strawberry.field(description="The kind of annotator that produced the annotation.") # type: ignore
|
|
39
|
+
async def annotator_kind(
|
|
40
|
+
self,
|
|
41
|
+
info: Info[Context, None],
|
|
42
|
+
) -> ExperimentRunAnnotatorKind:
|
|
43
|
+
if self.db_record:
|
|
44
|
+
val = self.db_record.annotator_kind
|
|
45
|
+
else:
|
|
46
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
47
|
+
(self.id, models.ExperimentRunAnnotation.annotator_kind),
|
|
48
|
+
)
|
|
49
|
+
return ExperimentRunAnnotatorKind(val)
|
|
50
|
+
|
|
51
|
+
@strawberry.field(
|
|
52
|
+
description="Value of the annotation in the form of a string, e.g. 'helpful' or 'not helpful'. Note that the label is not necessarily binary." # noqa: E501
|
|
53
|
+
) # type: ignore
|
|
54
|
+
async def label(
|
|
55
|
+
self,
|
|
56
|
+
info: Info[Context, None],
|
|
57
|
+
) -> Optional[str]:
|
|
58
|
+
if self.db_record:
|
|
59
|
+
val = self.db_record.label
|
|
60
|
+
else:
|
|
61
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
62
|
+
(self.id, models.ExperimentRunAnnotation.label),
|
|
63
|
+
)
|
|
64
|
+
return val
|
|
65
|
+
|
|
66
|
+
@strawberry.field(description="Value of the annotation in the form of a numeric score.") # type: ignore
|
|
67
|
+
async def score(
|
|
68
|
+
self,
|
|
69
|
+
info: Info[Context, None],
|
|
70
|
+
) -> Optional[float]:
|
|
71
|
+
if self.db_record:
|
|
72
|
+
val = self.db_record.score
|
|
73
|
+
else:
|
|
74
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
75
|
+
(self.id, models.ExperimentRunAnnotation.score),
|
|
76
|
+
)
|
|
77
|
+
return val if val is not None and isfinite(val) else None
|
|
78
|
+
|
|
79
|
+
@strawberry.field(
|
|
80
|
+
description="The annotator's explanation for the annotation result (i.e. score or label, or both) given to the subject." # noqa: E501
|
|
81
|
+
) # type: ignore
|
|
82
|
+
async def explanation(
|
|
83
|
+
self,
|
|
84
|
+
info: Info[Context, None],
|
|
85
|
+
) -> Optional[str]:
|
|
86
|
+
if self.db_record:
|
|
87
|
+
val = self.db_record.explanation
|
|
88
|
+
else:
|
|
89
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
90
|
+
(self.id, models.ExperimentRunAnnotation.explanation),
|
|
91
|
+
)
|
|
92
|
+
return val
|
|
93
|
+
|
|
94
|
+
@strawberry.field(description="Error message if the annotation failed to produce a result.") # type: ignore
|
|
95
|
+
async def error(
|
|
96
|
+
self,
|
|
97
|
+
info: Info[Context, None],
|
|
98
|
+
) -> Optional[str]:
|
|
99
|
+
if self.db_record:
|
|
100
|
+
val = self.db_record.error
|
|
101
|
+
else:
|
|
102
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
103
|
+
(self.id, models.ExperimentRunAnnotation.error),
|
|
104
|
+
)
|
|
105
|
+
return val
|
|
106
|
+
|
|
107
|
+
@strawberry.field(description="Metadata about the annotation.") # type: ignore
|
|
108
|
+
async def metadata(
|
|
109
|
+
self,
|
|
110
|
+
info: Info[Context, None],
|
|
111
|
+
) -> JSON:
|
|
112
|
+
if self.db_record:
|
|
113
|
+
val = self.db_record.metadata_
|
|
114
|
+
else:
|
|
115
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
116
|
+
(self.id, models.ExperimentRunAnnotation.metadata_),
|
|
117
|
+
)
|
|
118
|
+
return val
|
|
119
|
+
|
|
120
|
+
@strawberry.field(description="The date and time when the annotation was created.") # type: ignore
|
|
121
|
+
async def start_time(
|
|
122
|
+
self,
|
|
123
|
+
info: Info[Context, None],
|
|
124
|
+
) -> datetime:
|
|
125
|
+
if self.db_record:
|
|
126
|
+
val = self.db_record.start_time
|
|
127
|
+
else:
|
|
128
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
129
|
+
(self.id, models.ExperimentRunAnnotation.start_time),
|
|
130
|
+
)
|
|
131
|
+
return val
|
|
132
|
+
|
|
133
|
+
@strawberry.field(description="The date and time when the annotation was last updated.") # type: ignore
|
|
134
|
+
async def end_time(
|
|
135
|
+
self,
|
|
136
|
+
info: Info[Context, None],
|
|
137
|
+
) -> datetime:
|
|
138
|
+
if self.db_record:
|
|
139
|
+
val = self.db_record.end_time
|
|
140
|
+
else:
|
|
141
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
142
|
+
(self.id, models.ExperimentRunAnnotation.end_time),
|
|
143
|
+
)
|
|
144
|
+
return val
|
|
145
|
+
|
|
146
|
+
@strawberry.field(description="The identifier of the trace associated with the annotation.") # type: ignore
|
|
147
|
+
async def trace_id(
|
|
148
|
+
self,
|
|
149
|
+
info: Info[Context, None],
|
|
150
|
+
) -> Optional[GlobalID]:
|
|
151
|
+
if self.db_record:
|
|
152
|
+
val = self.db_record.trace_id
|
|
153
|
+
else:
|
|
154
|
+
val = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
155
|
+
(self.id, models.ExperimentRunAnnotation.trace_id),
|
|
156
|
+
)
|
|
157
|
+
return None if val is None else GlobalID(type_name=Trace.__name__, node_id=val)
|
|
158
|
+
|
|
159
|
+
@strawberry.field(description="The trace associated with the annotation.") # type: ignore
|
|
160
|
+
async def trace(
|
|
161
|
+
self,
|
|
162
|
+
info: Info[Context, None],
|
|
163
|
+
) -> Optional[Trace]:
|
|
164
|
+
if self.db_record:
|
|
165
|
+
trace_id = self.db_record.trace_id
|
|
166
|
+
else:
|
|
167
|
+
trace_id = await info.context.data_loaders.experiment_run_annotation_fields.load(
|
|
168
|
+
(self.id, models.ExperimentRunAnnotation.trace_id),
|
|
169
|
+
)
|
|
170
|
+
if not trace_id:
|
|
31
171
|
return None
|
|
32
172
|
dataloader = info.context.data_loaders.trace_by_trace_ids
|
|
33
|
-
if (trace := await dataloader.load(
|
|
173
|
+
if (trace := await dataloader.load(trace_id)) is None:
|
|
34
174
|
return None
|
|
35
|
-
return Trace(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def to_gql_experiment_run_annotation(
|
|
39
|
-
annotation: models.ExperimentRunAnnotation,
|
|
40
|
-
) -> ExperimentRunAnnotation:
|
|
41
|
-
"""
|
|
42
|
-
Converts an ORM experiment run annotation to a GraphQL ExperimentRunAnnotation.
|
|
43
|
-
"""
|
|
44
|
-
return ExperimentRunAnnotation(
|
|
45
|
-
id_attr=annotation.id,
|
|
46
|
-
name=annotation.name,
|
|
47
|
-
annotator_kind=ExperimentRunAnnotatorKind(annotation.annotator_kind),
|
|
48
|
-
label=annotation.label,
|
|
49
|
-
score=annotation.score,
|
|
50
|
-
explanation=annotation.explanation,
|
|
51
|
-
error=annotation.error,
|
|
52
|
-
metadata=annotation.metadata_,
|
|
53
|
-
start_time=annotation.start_time,
|
|
54
|
-
end_time=annotation.end_time,
|
|
55
|
-
trace_id=annotation.trace_id,
|
|
56
|
-
)
|
|
175
|
+
return Trace(id=trace.id, db_record=trace)
|