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
phoenix/db/models.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from datetime import datetime, timezone
|
|
2
3
|
from typing import Any, Iterable, Literal, Optional, Sequence, TypedDict, cast
|
|
3
4
|
|
|
5
|
+
import orjson
|
|
6
|
+
import sqlalchemy as sa
|
|
4
7
|
import sqlalchemy.sql as sql
|
|
5
8
|
from openinference.semconv.trace import RerankerAttributes, SpanAttributes
|
|
6
9
|
from sqlalchemy import (
|
|
7
10
|
JSON,
|
|
8
11
|
NUMERIC,
|
|
9
12
|
TIMESTAMP,
|
|
13
|
+
Boolean,
|
|
10
14
|
CheckConstraint,
|
|
11
15
|
ColumnElement,
|
|
12
16
|
Dialect,
|
|
@@ -16,6 +20,7 @@ from sqlalchemy import (
|
|
|
16
20
|
Integer,
|
|
17
21
|
MetaData,
|
|
18
22
|
Null,
|
|
23
|
+
PrimaryKeyConstraint,
|
|
19
24
|
String,
|
|
20
25
|
TypeDecorator,
|
|
21
26
|
UniqueConstraint,
|
|
@@ -39,6 +44,7 @@ from sqlalchemy.orm import (
|
|
|
39
44
|
)
|
|
40
45
|
from sqlalchemy.sql import Values, column, compiler, expression, literal, roles, union_all
|
|
41
46
|
from sqlalchemy.sql.compiler import SQLCompiler
|
|
47
|
+
from sqlalchemy.sql.elements import Case
|
|
42
48
|
from sqlalchemy.sql.functions import coalesce
|
|
43
49
|
from typing_extensions import TypeAlias
|
|
44
50
|
|
|
@@ -52,6 +58,10 @@ from phoenix.db.types.annotation_configs import (
|
|
|
52
58
|
)
|
|
53
59
|
from phoenix.db.types.identifier import Identifier
|
|
54
60
|
from phoenix.db.types.model_provider import ModelProvider
|
|
61
|
+
from phoenix.db.types.token_price_customization import (
|
|
62
|
+
TokenPriceCustomization,
|
|
63
|
+
TokenPriceCustomizationParser,
|
|
64
|
+
)
|
|
55
65
|
from phoenix.db.types.trace_retention import TraceRetentionCronExpression, TraceRetentionRule
|
|
56
66
|
from phoenix.server.api.helpers.prompts.models import (
|
|
57
67
|
PromptInvocationParameters,
|
|
@@ -146,6 +156,7 @@ def render_values_w_union(
|
|
|
146
156
|
return compiler.process(subquery, from_linter=from_linter, **kw)
|
|
147
157
|
|
|
148
158
|
|
|
159
|
+
UserRoleName: TypeAlias = Literal["SYSTEM", "ADMIN", "MEMBER", "VIEWER"]
|
|
149
160
|
AuthMethod: TypeAlias = Literal["LOCAL", "OAUTH2"]
|
|
150
161
|
|
|
151
162
|
|
|
@@ -181,6 +192,9 @@ class JsonDict(TypeDecorator[dict[str, Any]]):
|
|
|
181
192
|
def process_bind_param(self, value: Optional[dict[str, Any]], _: Dialect) -> dict[str, Any]:
|
|
182
193
|
return value if isinstance(value, dict) else {}
|
|
183
194
|
|
|
195
|
+
def process_result_value(self, value: Optional[Any], _: Dialect) -> Optional[dict[str, Any]]:
|
|
196
|
+
return orjson.loads(orjson.dumps(value)) if isinstance(value, dict) and value else value
|
|
197
|
+
|
|
184
198
|
|
|
185
199
|
class JsonList(TypeDecorator[list[Any]]):
|
|
186
200
|
# See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
@@ -190,6 +204,9 @@ class JsonList(TypeDecorator[list[Any]]):
|
|
|
190
204
|
def process_bind_param(self, value: Optional[list[Any]], _: Dialect) -> list[Any]:
|
|
191
205
|
return value if isinstance(value, list) else []
|
|
192
206
|
|
|
207
|
+
def process_result_value(self, value: Optional[Any], _: Dialect) -> Optional[list[Any]]:
|
|
208
|
+
return orjson.loads(orjson.dumps(value)) if isinstance(value, list) and value else value
|
|
209
|
+
|
|
193
210
|
|
|
194
211
|
class UtcTimeStamp(TypeDecorator[datetime]):
|
|
195
212
|
# See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
@@ -390,12 +407,69 @@ class _AnnotationConfig(TypeDecorator[AnnotationConfigType]):
|
|
|
390
407
|
return AnnotationConfigModel.model_validate(value).root if value is not None else None
|
|
391
408
|
|
|
392
409
|
|
|
410
|
+
class _TokenCustomization(TypeDecorator[TokenPriceCustomization]):
|
|
411
|
+
# See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
412
|
+
cache_ok = True
|
|
413
|
+
impl = JSON
|
|
414
|
+
|
|
415
|
+
def process_bind_param(
|
|
416
|
+
self, value: Optional[TokenPriceCustomization], _: Dialect
|
|
417
|
+
) -> Optional[dict[str, Any]]:
|
|
418
|
+
return value.model_dump() if value is not None else None
|
|
419
|
+
|
|
420
|
+
def process_result_value(
|
|
421
|
+
self, value: Optional[dict[str, Any]], _: Dialect
|
|
422
|
+
) -> Optional[TokenPriceCustomization]:
|
|
423
|
+
return TokenPriceCustomizationParser.parse(value)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class _RegexStr(TypeDecorator[re.Pattern[str]]):
|
|
427
|
+
# See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
428
|
+
cache_ok = True
|
|
429
|
+
impl = String
|
|
430
|
+
|
|
431
|
+
def process_bind_param(self, value: Optional[re.Pattern[str]], _: Dialect) -> Optional[str]:
|
|
432
|
+
if value is None:
|
|
433
|
+
return None
|
|
434
|
+
if not isinstance(value, re.Pattern):
|
|
435
|
+
raise TypeError(f"Expected a regex pattern, got {type(value)}")
|
|
436
|
+
pattern = value.pattern
|
|
437
|
+
if not isinstance(pattern, str):
|
|
438
|
+
raise ValueError(f"Expected a string, got {type(pattern)}")
|
|
439
|
+
return pattern
|
|
440
|
+
|
|
441
|
+
def process_result_value(self, value: Optional[str], _: Dialect) -> Optional[re.Pattern[str]]:
|
|
442
|
+
if value is None:
|
|
443
|
+
return None
|
|
444
|
+
return re.compile(value)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
_HEX_COLOR_PATTERN = re.compile(r"^#([0-9a-f]{6})$")
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class _HexColor(TypeDecorator[str]):
|
|
451
|
+
# See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
452
|
+
cache_ok = True
|
|
453
|
+
impl = String
|
|
454
|
+
|
|
455
|
+
def process_bind_param(self, value: Optional[str], _: Dialect) -> Optional[str]:
|
|
456
|
+
if value is None:
|
|
457
|
+
return None
|
|
458
|
+
if not _HEX_COLOR_PATTERN.match(value):
|
|
459
|
+
raise ValueError(f"Expected a hex color, got {value}")
|
|
460
|
+
return value
|
|
461
|
+
|
|
462
|
+
def process_result_value(self, value: Optional[str], _: Dialect) -> Optional[str]:
|
|
463
|
+
if value is None:
|
|
464
|
+
return None
|
|
465
|
+
return value
|
|
466
|
+
|
|
467
|
+
|
|
393
468
|
class ExperimentRunOutput(TypedDict, total=False):
|
|
394
469
|
task_output: Any
|
|
395
470
|
|
|
396
471
|
|
|
397
472
|
class Base(DeclarativeBase):
|
|
398
|
-
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
399
473
|
# Enforce best practices for naming constraints
|
|
400
474
|
# https://alembic.sqlalchemy.org/en/latest/naming.html#integration-of-naming-conventions-into-operations-autogenerate
|
|
401
475
|
metadata = MetaData(
|
|
@@ -415,9 +489,13 @@ class Base(DeclarativeBase):
|
|
|
415
489
|
}
|
|
416
490
|
|
|
417
491
|
|
|
418
|
-
class
|
|
492
|
+
class HasId(Base):
|
|
493
|
+
__abstract__ = True
|
|
494
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
class ProjectTraceRetentionPolicy(HasId):
|
|
419
498
|
__tablename__ = "project_trace_retention_policies"
|
|
420
|
-
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
421
499
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
|
422
500
|
cron_expression: Mapped[TraceRetentionCronExpression] = mapped_column(
|
|
423
501
|
_TraceRetentionCronExpression, nullable=False
|
|
@@ -428,7 +506,7 @@ class ProjectTraceRetentionPolicy(Base):
|
|
|
428
506
|
)
|
|
429
507
|
|
|
430
508
|
|
|
431
|
-
class Project(
|
|
509
|
+
class Project(HasId):
|
|
432
510
|
__tablename__ = "projects"
|
|
433
511
|
name: Mapped[str]
|
|
434
512
|
description: Mapped[Optional[str]]
|
|
@@ -468,7 +546,7 @@ class Project(Base):
|
|
|
468
546
|
)
|
|
469
547
|
|
|
470
548
|
|
|
471
|
-
class ProjectSession(
|
|
549
|
+
class ProjectSession(HasId):
|
|
472
550
|
__tablename__ = "project_sessions"
|
|
473
551
|
session_id: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
|
474
552
|
project_id: Mapped[int] = mapped_column(
|
|
@@ -485,7 +563,7 @@ class ProjectSession(Base):
|
|
|
485
563
|
)
|
|
486
564
|
|
|
487
565
|
|
|
488
|
-
class Trace(
|
|
566
|
+
class Trace(HasId):
|
|
489
567
|
__tablename__ = "traces"
|
|
490
568
|
project_rowid: Mapped[int] = mapped_column(
|
|
491
569
|
ForeignKey("projects.id", ondelete="CASCADE"),
|
|
@@ -527,6 +605,12 @@ class Trace(Base):
|
|
|
527
605
|
primaryjoin="foreign(ExperimentRun.trace_id) == Trace.trace_id",
|
|
528
606
|
back_populates="trace",
|
|
529
607
|
)
|
|
608
|
+
span_costs: Mapped[list["SpanCost"]] = relationship(
|
|
609
|
+
"SpanCost",
|
|
610
|
+
back_populates="trace",
|
|
611
|
+
cascade="all, delete-orphan",
|
|
612
|
+
uselist=True,
|
|
613
|
+
)
|
|
530
614
|
__table_args__ = (
|
|
531
615
|
UniqueConstraint(
|
|
532
616
|
"trace_id",
|
|
@@ -534,13 +618,13 @@ class Trace(Base):
|
|
|
534
618
|
)
|
|
535
619
|
|
|
536
620
|
|
|
537
|
-
class Span(
|
|
621
|
+
class Span(HasId):
|
|
538
622
|
__tablename__ = "spans"
|
|
539
623
|
trace_rowid: Mapped[int] = mapped_column(
|
|
540
624
|
ForeignKey("traces.id", ondelete="CASCADE"),
|
|
541
625
|
index=True,
|
|
542
626
|
)
|
|
543
|
-
span_id: Mapped[str]
|
|
627
|
+
span_id: Mapped[str]
|
|
544
628
|
parent_id: Mapped[Optional[str]] = mapped_column(index=True)
|
|
545
629
|
name: Mapped[str]
|
|
546
630
|
span_kind: Mapped[str]
|
|
@@ -684,6 +768,7 @@ class Span(Base):
|
|
|
684
768
|
span_annotations: Mapped[list["SpanAnnotation"]] = relationship(back_populates="span")
|
|
685
769
|
document_annotations: Mapped[list["DocumentAnnotation"]] = relationship(back_populates="span")
|
|
686
770
|
dataset_examples: Mapped[list["DatasetExample"]] = relationship(back_populates="span")
|
|
771
|
+
span_cost: Mapped[Optional["SpanCost"]] = relationship(back_populates="span")
|
|
687
772
|
|
|
688
773
|
__table_args__ = (
|
|
689
774
|
UniqueConstraint(
|
|
@@ -745,14 +830,27 @@ class NumDocuments(expression.FunctionElement[int]):
|
|
|
745
830
|
@compiles(NumDocuments)
|
|
746
831
|
def _(element: Any, compiler: SQLCompiler, **kw: Any) -> Any:
|
|
747
832
|
# See https://docs.sqlalchemy.org/en/20/core/compiler.html
|
|
748
|
-
array_length = (
|
|
749
|
-
func.json_array_length if isinstance(compiler, SQLiteCompiler) else func.jsonb_array_length
|
|
750
|
-
)
|
|
751
833
|
attributes, span_kind = list(element.clauses)
|
|
752
834
|
retrieval_docs = attributes[RETRIEVAL_DOCUMENTS]
|
|
753
|
-
num_retrieval_docs
|
|
835
|
+
num_retrieval_docs: Case[Any] | coalesce[Any]
|
|
754
836
|
reranker_docs = attributes[RERANKER_OUTPUT_DOCUMENTS]
|
|
755
|
-
num_reranker_docs
|
|
837
|
+
num_reranker_docs: Case[Any] | coalesce[Any]
|
|
838
|
+
if isinstance(compiler, SQLiteCompiler):
|
|
839
|
+
# SQLite's json_array_length returns 0 for non-array values
|
|
840
|
+
num_retrieval_docs = coalesce(func.json_array_length(retrieval_docs), 0)
|
|
841
|
+
num_reranker_docs = coalesce(func.json_array_length(reranker_docs), 0)
|
|
842
|
+
else:
|
|
843
|
+
# PostgreSQL's jsonb_array_length throws "cannot get array length of a scalar"
|
|
844
|
+
# for non-array values, so check the type first
|
|
845
|
+
num_retrieval_docs = sql.case(
|
|
846
|
+
(func.jsonb_typeof(retrieval_docs) == "array", func.jsonb_array_length(retrieval_docs)),
|
|
847
|
+
else_=0,
|
|
848
|
+
)
|
|
849
|
+
num_reranker_docs = sql.case(
|
|
850
|
+
(func.jsonb_typeof(reranker_docs) == "array", func.jsonb_array_length(reranker_docs)),
|
|
851
|
+
else_=0,
|
|
852
|
+
)
|
|
853
|
+
|
|
756
854
|
return compiler.process(
|
|
757
855
|
sql.case(
|
|
758
856
|
(func.upper(span_kind) == "RERANKER", num_reranker_docs),
|
|
@@ -790,6 +888,41 @@ def _(element: Any, compiler: Any, **kw: Any) -> Any:
|
|
|
790
888
|
return compiler.process(func.text_contains(string, substring) > 0, **kw)
|
|
791
889
|
|
|
792
890
|
|
|
891
|
+
class CaseInsensitiveContains(expression.FunctionElement[bool]):
|
|
892
|
+
# See https://docs.sqlalchemy.org/en/20/core/compiler.html
|
|
893
|
+
inherit_cache = True
|
|
894
|
+
type = Boolean()
|
|
895
|
+
name = "case_insensitive_contains"
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
@compiles(CaseInsensitiveContains)
|
|
899
|
+
def _(element: Any, compiler: Any, **kw: Any) -> Any:
|
|
900
|
+
string, substring = list(element.clauses)
|
|
901
|
+
result = compiler.process(func.lower(string).contains(func.lower(substring)), **kw)
|
|
902
|
+
return result
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
@compiles(CaseInsensitiveContains, "postgresql")
|
|
906
|
+
def _(element: Any, compiler: Any, **kw: Any) -> Any:
|
|
907
|
+
string, substring = list(element.clauses)
|
|
908
|
+
escaped = func.replace(
|
|
909
|
+
func.replace(func.replace(substring, "\\", "\\\\"), "%", "\\%"), "_", "\\_"
|
|
910
|
+
)
|
|
911
|
+
pattern = func.concat("%", escaped, "%")
|
|
912
|
+
result = compiler.process(string.ilike(pattern), **kw)
|
|
913
|
+
return result
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
@compiles(CaseInsensitiveContains, "sqlite")
|
|
917
|
+
def _(element: Any, compiler: Any, **kw: Any) -> Any:
|
|
918
|
+
# Use sqlean's `text_lower` to handle non-ASCII characters
|
|
919
|
+
string, substring = list(element.clauses)
|
|
920
|
+
result = compiler.process(
|
|
921
|
+
func.text_contains(func.text_lower(string), func.text_lower(substring)), **kw
|
|
922
|
+
)
|
|
923
|
+
return result
|
|
924
|
+
|
|
925
|
+
|
|
793
926
|
async def init_models(engine: AsyncEngine) -> None:
|
|
794
927
|
async with engine.begin() as conn:
|
|
795
928
|
await conn.run_sync(Base.metadata.create_all)
|
|
@@ -801,15 +934,15 @@ async def init_models(engine: AsyncEngine) -> None:
|
|
|
801
934
|
)
|
|
802
935
|
|
|
803
936
|
|
|
804
|
-
class SpanAnnotation(
|
|
937
|
+
class SpanAnnotation(HasId):
|
|
805
938
|
__tablename__ = "span_annotations"
|
|
806
939
|
span_rowid: Mapped[int] = mapped_column(
|
|
807
940
|
ForeignKey("spans.id", ondelete="CASCADE"),
|
|
808
941
|
index=True,
|
|
809
942
|
)
|
|
810
943
|
name: Mapped[str]
|
|
811
|
-
label: Mapped[Optional[str]]
|
|
812
|
-
score: Mapped[Optional[float]]
|
|
944
|
+
label: Mapped[Optional[str]]
|
|
945
|
+
score: Mapped[Optional[float]]
|
|
813
946
|
explanation: Mapped[Optional[str]]
|
|
814
947
|
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
815
948
|
annotator_kind: Mapped[Literal["LLM", "CODE", "HUMAN"]] = mapped_column(
|
|
@@ -841,15 +974,15 @@ class SpanAnnotation(Base):
|
|
|
841
974
|
)
|
|
842
975
|
|
|
843
976
|
|
|
844
|
-
class TraceAnnotation(
|
|
977
|
+
class TraceAnnotation(HasId):
|
|
845
978
|
__tablename__ = "trace_annotations"
|
|
846
979
|
trace_rowid: Mapped[int] = mapped_column(
|
|
847
980
|
ForeignKey("traces.id", ondelete="CASCADE"),
|
|
848
981
|
index=True,
|
|
849
982
|
)
|
|
850
983
|
name: Mapped[str]
|
|
851
|
-
label: Mapped[Optional[str]]
|
|
852
|
-
score: Mapped[Optional[float]]
|
|
984
|
+
label: Mapped[Optional[str]]
|
|
985
|
+
score: Mapped[Optional[float]]
|
|
853
986
|
explanation: Mapped[Optional[str]]
|
|
854
987
|
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
855
988
|
annotator_kind: Mapped[Literal["LLM", "CODE", "HUMAN"]] = mapped_column(
|
|
@@ -878,7 +1011,7 @@ class TraceAnnotation(Base):
|
|
|
878
1011
|
)
|
|
879
1012
|
|
|
880
1013
|
|
|
881
|
-
class DocumentAnnotation(
|
|
1014
|
+
class DocumentAnnotation(HasId):
|
|
882
1015
|
__tablename__ = "document_annotations"
|
|
883
1016
|
span_rowid: Mapped[int] = mapped_column(
|
|
884
1017
|
ForeignKey("spans.id", ondelete="CASCADE"),
|
|
@@ -886,8 +1019,8 @@ class DocumentAnnotation(Base):
|
|
|
886
1019
|
)
|
|
887
1020
|
document_position: Mapped[int]
|
|
888
1021
|
name: Mapped[str]
|
|
889
|
-
label: Mapped[Optional[str]]
|
|
890
|
-
score: Mapped[Optional[float]]
|
|
1022
|
+
label: Mapped[Optional[str]]
|
|
1023
|
+
score: Mapped[Optional[float]]
|
|
891
1024
|
explanation: Mapped[Optional[str]]
|
|
892
1025
|
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
893
1026
|
annotator_kind: Mapped[Literal["LLM", "CODE", "HUMAN"]] = mapped_column(
|
|
@@ -919,7 +1052,44 @@ class DocumentAnnotation(Base):
|
|
|
919
1052
|
)
|
|
920
1053
|
|
|
921
1054
|
|
|
922
|
-
class
|
|
1055
|
+
class ProjectSessionAnnotation(HasId):
|
|
1056
|
+
__tablename__ = "project_session_annotations"
|
|
1057
|
+
project_session_id: Mapped[int] = mapped_column(
|
|
1058
|
+
ForeignKey("project_sessions.id", ondelete="CASCADE"),
|
|
1059
|
+
index=True,
|
|
1060
|
+
)
|
|
1061
|
+
name: Mapped[str]
|
|
1062
|
+
label: Mapped[Optional[str]]
|
|
1063
|
+
score: Mapped[Optional[float]]
|
|
1064
|
+
explanation: Mapped[Optional[str]]
|
|
1065
|
+
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
1066
|
+
annotator_kind: Mapped[Literal["LLM", "CODE", "HUMAN"]] = mapped_column(
|
|
1067
|
+
CheckConstraint("annotator_kind IN ('LLM', 'CODE', 'HUMAN')", name="valid_annotator_kind"),
|
|
1068
|
+
)
|
|
1069
|
+
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1070
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
1071
|
+
UtcTimeStamp, server_default=func.now(), onupdate=func.now()
|
|
1072
|
+
)
|
|
1073
|
+
identifier: Mapped[str] = mapped_column(
|
|
1074
|
+
String,
|
|
1075
|
+
server_default="",
|
|
1076
|
+
nullable=False,
|
|
1077
|
+
)
|
|
1078
|
+
source: Mapped[Literal["API", "APP"]] = mapped_column(
|
|
1079
|
+
CheckConstraint("source IN ('API', 'APP')", name="valid_source"),
|
|
1080
|
+
)
|
|
1081
|
+
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
1082
|
+
|
|
1083
|
+
__table_args__ = (
|
|
1084
|
+
UniqueConstraint(
|
|
1085
|
+
"name",
|
|
1086
|
+
"project_session_id",
|
|
1087
|
+
"identifier",
|
|
1088
|
+
),
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
class Dataset(HasId):
|
|
923
1093
|
__tablename__ = "datasets"
|
|
924
1094
|
name: Mapped[str] = mapped_column(unique=True)
|
|
925
1095
|
description: Mapped[Optional[str]]
|
|
@@ -928,6 +1098,14 @@ class Dataset(Base):
|
|
|
928
1098
|
updated_at: Mapped[datetime] = mapped_column(
|
|
929
1099
|
UtcTimeStamp, server_default=func.now(), onupdate=func.now()
|
|
930
1100
|
)
|
|
1101
|
+
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
1102
|
+
user: Mapped[Optional["User"]] = relationship("User")
|
|
1103
|
+
experiment_tags: Mapped[list["ExperimentTag"]] = relationship(
|
|
1104
|
+
"ExperimentTag", back_populates="dataset"
|
|
1105
|
+
)
|
|
1106
|
+
datasets_dataset_labels: Mapped[list["DatasetsDatasetLabel"]] = relationship(
|
|
1107
|
+
"DatasetsDatasetLabel", back_populates="dataset"
|
|
1108
|
+
)
|
|
931
1109
|
|
|
932
1110
|
@hybrid_property
|
|
933
1111
|
def example_count(self) -> Optional[int]:
|
|
@@ -978,7 +1156,45 @@ class Dataset(Base):
|
|
|
978
1156
|
)
|
|
979
1157
|
|
|
980
1158
|
|
|
981
|
-
class
|
|
1159
|
+
class DatasetLabel(HasId):
|
|
1160
|
+
__tablename__ = "dataset_labels"
|
|
1161
|
+
name: Mapped[str] = mapped_column(unique=True)
|
|
1162
|
+
description: Mapped[Optional[str]]
|
|
1163
|
+
color: Mapped[str] = mapped_column(_HexColor, nullable=False)
|
|
1164
|
+
datasets_dataset_labels: Mapped[list["DatasetsDatasetLabel"]] = relationship(
|
|
1165
|
+
"DatasetsDatasetLabel", back_populates="dataset_label"
|
|
1166
|
+
)
|
|
1167
|
+
user_id: Mapped[Optional[int]] = mapped_column(
|
|
1168
|
+
ForeignKey("users.id", ondelete="SET NULL"),
|
|
1169
|
+
nullable=True,
|
|
1170
|
+
)
|
|
1171
|
+
user: Mapped[Optional["User"]] = relationship("User")
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
class DatasetsDatasetLabel(Base):
|
|
1175
|
+
__tablename__ = "datasets_dataset_labels"
|
|
1176
|
+
dataset_id: Mapped[int] = mapped_column(
|
|
1177
|
+
ForeignKey("datasets.id", ondelete="CASCADE"),
|
|
1178
|
+
)
|
|
1179
|
+
dataset_label_id: Mapped[int] = mapped_column(
|
|
1180
|
+
ForeignKey("dataset_labels.id", ondelete="CASCADE"),
|
|
1181
|
+
# index on the second element of the composite primary key
|
|
1182
|
+
index=True,
|
|
1183
|
+
)
|
|
1184
|
+
dataset: Mapped["Dataset"] = relationship("Dataset", back_populates="datasets_dataset_labels")
|
|
1185
|
+
dataset_label: Mapped["DatasetLabel"] = relationship(
|
|
1186
|
+
"DatasetLabel", back_populates="datasets_dataset_labels"
|
|
1187
|
+
)
|
|
1188
|
+
|
|
1189
|
+
__table_args__ = (
|
|
1190
|
+
PrimaryKeyConstraint(
|
|
1191
|
+
"dataset_id",
|
|
1192
|
+
"dataset_label_id",
|
|
1193
|
+
),
|
|
1194
|
+
)
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
class DatasetVersion(HasId):
|
|
982
1198
|
__tablename__ = "dataset_versions"
|
|
983
1199
|
dataset_id: Mapped[int] = mapped_column(
|
|
984
1200
|
ForeignKey("datasets.id", ondelete="CASCADE"),
|
|
@@ -987,9 +1203,11 @@ class DatasetVersion(Base):
|
|
|
987
1203
|
description: Mapped[Optional[str]]
|
|
988
1204
|
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
989
1205
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1206
|
+
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
1207
|
+
user: Mapped[Optional["User"]] = relationship("User")
|
|
990
1208
|
|
|
991
1209
|
|
|
992
|
-
class DatasetExample(
|
|
1210
|
+
class DatasetExample(HasId):
|
|
993
1211
|
__tablename__ = "dataset_examples"
|
|
994
1212
|
dataset_id: Mapped[int] = mapped_column(
|
|
995
1213
|
ForeignKey("datasets.id", ondelete="CASCADE"),
|
|
@@ -1003,13 +1221,20 @@ class DatasetExample(Base):
|
|
|
1003
1221
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1004
1222
|
|
|
1005
1223
|
span: Mapped[Optional[Span]] = relationship(back_populates="dataset_examples")
|
|
1224
|
+
dataset_splits_dataset_examples: Mapped[list["DatasetSplitDatasetExample"]] = relationship(
|
|
1225
|
+
"DatasetSplitDatasetExample",
|
|
1226
|
+
back_populates="dataset_example",
|
|
1227
|
+
)
|
|
1228
|
+
experiment_dataset_examples: Mapped[list["ExperimentDatasetExample"]] = relationship(
|
|
1229
|
+
"ExperimentDatasetExample",
|
|
1230
|
+
back_populates="dataset_example",
|
|
1231
|
+
)
|
|
1006
1232
|
|
|
1007
1233
|
|
|
1008
|
-
class DatasetExampleRevision(
|
|
1234
|
+
class DatasetExampleRevision(HasId):
|
|
1009
1235
|
__tablename__ = "dataset_example_revisions"
|
|
1010
1236
|
dataset_example_id: Mapped[int] = mapped_column(
|
|
1011
1237
|
ForeignKey("dataset_examples.id", ondelete="CASCADE"),
|
|
1012
|
-
index=True,
|
|
1013
1238
|
)
|
|
1014
1239
|
dataset_version_id: Mapped[int] = mapped_column(
|
|
1015
1240
|
ForeignKey("dataset_versions.id", ondelete="CASCADE"),
|
|
@@ -1025,6 +1250,11 @@ class DatasetExampleRevision(Base):
|
|
|
1025
1250
|
)
|
|
1026
1251
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1027
1252
|
|
|
1253
|
+
experiment_dataset_examples: Mapped[list["ExperimentDatasetExample"]] = relationship(
|
|
1254
|
+
"ExperimentDatasetExample",
|
|
1255
|
+
back_populates="dataset_example_revision",
|
|
1256
|
+
)
|
|
1257
|
+
|
|
1028
1258
|
__table_args__ = (
|
|
1029
1259
|
UniqueConstraint(
|
|
1030
1260
|
"dataset_example_id",
|
|
@@ -1033,7 +1263,56 @@ class DatasetExampleRevision(Base):
|
|
|
1033
1263
|
)
|
|
1034
1264
|
|
|
1035
1265
|
|
|
1036
|
-
class
|
|
1266
|
+
class DatasetSplit(HasId):
|
|
1267
|
+
__tablename__ = "dataset_splits"
|
|
1268
|
+
|
|
1269
|
+
user_id: Mapped[Optional[int]] = mapped_column(
|
|
1270
|
+
ForeignKey("users.id", ondelete="SET NULL"),
|
|
1271
|
+
nullable=True,
|
|
1272
|
+
index=True,
|
|
1273
|
+
)
|
|
1274
|
+
name: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
|
1275
|
+
description: Mapped[Optional[str]]
|
|
1276
|
+
color: Mapped[str] = mapped_column(String, nullable=False)
|
|
1277
|
+
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
1278
|
+
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1279
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
1280
|
+
UtcTimeStamp, server_default=func.now(), onupdate=func.now()
|
|
1281
|
+
)
|
|
1282
|
+
dataset_splits_dataset_examples: Mapped[list["DatasetSplitDatasetExample"]] = relationship(
|
|
1283
|
+
"DatasetSplitDatasetExample",
|
|
1284
|
+
back_populates="dataset_split",
|
|
1285
|
+
)
|
|
1286
|
+
experiment_dataset_splits: Mapped[list["ExperimentDatasetSplit"]] = relationship(
|
|
1287
|
+
"ExperimentDatasetSplit",
|
|
1288
|
+
back_populates="dataset_split",
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1291
|
+
|
|
1292
|
+
class DatasetSplitDatasetExample(Base):
|
|
1293
|
+
__tablename__ = "dataset_splits_dataset_examples"
|
|
1294
|
+
dataset_split_id: Mapped[int] = mapped_column(
|
|
1295
|
+
ForeignKey("dataset_splits.id", ondelete="CASCADE"),
|
|
1296
|
+
)
|
|
1297
|
+
dataset_example_id: Mapped[int] = mapped_column(
|
|
1298
|
+
ForeignKey("dataset_examples.id", ondelete="CASCADE"),
|
|
1299
|
+
index=True,
|
|
1300
|
+
)
|
|
1301
|
+
dataset_split: Mapped["DatasetSplit"] = relationship(
|
|
1302
|
+
"DatasetSplit", back_populates="dataset_splits_dataset_examples"
|
|
1303
|
+
)
|
|
1304
|
+
dataset_example: Mapped["DatasetExample"] = relationship(
|
|
1305
|
+
"DatasetExample", back_populates="dataset_splits_dataset_examples"
|
|
1306
|
+
)
|
|
1307
|
+
__table_args__ = (
|
|
1308
|
+
PrimaryKeyConstraint(
|
|
1309
|
+
"dataset_split_id",
|
|
1310
|
+
"dataset_example_id",
|
|
1311
|
+
),
|
|
1312
|
+
)
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
class Experiment(HasId):
|
|
1037
1316
|
__tablename__ = "experiments"
|
|
1038
1317
|
dataset_id: Mapped[int] = mapped_column(
|
|
1039
1318
|
ForeignKey("datasets.id", ondelete="CASCADE"),
|
|
@@ -1048,18 +1327,83 @@ class Experiment(Base):
|
|
|
1048
1327
|
repetitions: Mapped[int]
|
|
1049
1328
|
metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
|
|
1050
1329
|
project_name: Mapped[Optional[str]] = mapped_column(index=True)
|
|
1330
|
+
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
1051
1331
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1052
1332
|
updated_at: Mapped[datetime] = mapped_column(
|
|
1053
1333
|
UtcTimeStamp, server_default=func.now(), onupdate=func.now()
|
|
1054
1334
|
)
|
|
1335
|
+
user: Mapped[Optional["User"]] = relationship("User")
|
|
1336
|
+
experiment_dataset_splits: Mapped[list["ExperimentDatasetSplit"]] = relationship(
|
|
1337
|
+
"ExperimentDatasetSplit",
|
|
1338
|
+
back_populates="experiment",
|
|
1339
|
+
)
|
|
1340
|
+
experiment_dataset_examples: Mapped[list["ExperimentDatasetExample"]] = relationship(
|
|
1341
|
+
"ExperimentDatasetExample",
|
|
1342
|
+
back_populates="experiment",
|
|
1343
|
+
)
|
|
1344
|
+
experiment_tags: Mapped[list["ExperimentTag"]] = relationship(
|
|
1345
|
+
"ExperimentTag", back_populates="experiment"
|
|
1346
|
+
)
|
|
1055
1347
|
|
|
1056
1348
|
|
|
1057
|
-
class
|
|
1058
|
-
__tablename__ = "
|
|
1349
|
+
class ExperimentDatasetSplit(Base):
|
|
1350
|
+
__tablename__ = "experiments_dataset_splits"
|
|
1059
1351
|
experiment_id: Mapped[int] = mapped_column(
|
|
1060
1352
|
ForeignKey("experiments.id", ondelete="CASCADE"),
|
|
1353
|
+
)
|
|
1354
|
+
dataset_split_id: Mapped[int] = mapped_column(
|
|
1355
|
+
ForeignKey("dataset_splits.id", ondelete="CASCADE"),
|
|
1356
|
+
index=True,
|
|
1357
|
+
)
|
|
1358
|
+
experiment: Mapped["Experiment"] = relationship(
|
|
1359
|
+
"Experiment", back_populates="experiment_dataset_splits"
|
|
1360
|
+
)
|
|
1361
|
+
dataset_split: Mapped["DatasetSplit"] = relationship(
|
|
1362
|
+
"DatasetSplit", back_populates="experiment_dataset_splits"
|
|
1363
|
+
)
|
|
1364
|
+
__table_args__ = (
|
|
1365
|
+
PrimaryKeyConstraint(
|
|
1366
|
+
"experiment_id",
|
|
1367
|
+
"dataset_split_id",
|
|
1368
|
+
),
|
|
1369
|
+
)
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
class ExperimentDatasetExample(Base):
|
|
1373
|
+
__tablename__ = "experiments_dataset_examples"
|
|
1374
|
+
experiment_id: Mapped[int] = mapped_column(
|
|
1375
|
+
ForeignKey("experiments.id", ondelete="CASCADE"),
|
|
1376
|
+
)
|
|
1377
|
+
dataset_example_id: Mapped[int] = mapped_column(
|
|
1378
|
+
ForeignKey("dataset_examples.id", ondelete="CASCADE"),
|
|
1061
1379
|
index=True,
|
|
1062
1380
|
)
|
|
1381
|
+
dataset_example_revision_id: Mapped[int] = mapped_column(
|
|
1382
|
+
ForeignKey("dataset_example_revisions.id", ondelete="CASCADE"),
|
|
1383
|
+
index=True,
|
|
1384
|
+
)
|
|
1385
|
+
experiment: Mapped["Experiment"] = relationship(
|
|
1386
|
+
"Experiment", back_populates="experiment_dataset_examples"
|
|
1387
|
+
)
|
|
1388
|
+
dataset_example: Mapped["DatasetExample"] = relationship(
|
|
1389
|
+
"DatasetExample", back_populates="experiment_dataset_examples"
|
|
1390
|
+
)
|
|
1391
|
+
dataset_example_revision: Mapped["DatasetExampleRevision"] = relationship(
|
|
1392
|
+
"DatasetExampleRevision", back_populates="experiment_dataset_examples"
|
|
1393
|
+
)
|
|
1394
|
+
__table_args__ = (
|
|
1395
|
+
PrimaryKeyConstraint(
|
|
1396
|
+
"experiment_id",
|
|
1397
|
+
"dataset_example_id",
|
|
1398
|
+
),
|
|
1399
|
+
)
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
class ExperimentRun(HasId):
|
|
1403
|
+
__tablename__ = "experiment_runs"
|
|
1404
|
+
experiment_id: Mapped[int] = mapped_column(
|
|
1405
|
+
ForeignKey("experiments.id", ondelete="CASCADE"),
|
|
1406
|
+
)
|
|
1063
1407
|
dataset_example_id: Mapped[int] = mapped_column(
|
|
1064
1408
|
ForeignKey("dataset_examples.id", ondelete="CASCADE"),
|
|
1065
1409
|
index=True,
|
|
@@ -1099,11 +1443,10 @@ class ExperimentRun(Base):
|
|
|
1099
1443
|
)
|
|
1100
1444
|
|
|
1101
1445
|
|
|
1102
|
-
class ExperimentRunAnnotation(
|
|
1446
|
+
class ExperimentRunAnnotation(HasId):
|
|
1103
1447
|
__tablename__ = "experiment_run_annotations"
|
|
1104
1448
|
experiment_run_id: Mapped[int] = mapped_column(
|
|
1105
1449
|
ForeignKey("experiment_runs.id", ondelete="CASCADE"),
|
|
1106
|
-
index=True,
|
|
1107
1450
|
)
|
|
1108
1451
|
name: Mapped[str]
|
|
1109
1452
|
annotator_kind: Mapped[str] = mapped_column(
|
|
@@ -1130,13 +1473,36 @@ class ExperimentRunAnnotation(Base):
|
|
|
1130
1473
|
)
|
|
1131
1474
|
|
|
1132
1475
|
|
|
1133
|
-
class
|
|
1476
|
+
class ExperimentTag(HasId):
|
|
1477
|
+
__tablename__ = "experiment_tags"
|
|
1478
|
+
experiment_id: Mapped[int] = mapped_column(
|
|
1479
|
+
ForeignKey("experiments.id", ondelete="CASCADE"),
|
|
1480
|
+
index=True,
|
|
1481
|
+
)
|
|
1482
|
+
dataset_id: Mapped[int] = mapped_column(
|
|
1483
|
+
ForeignKey("datasets.id", ondelete="CASCADE"),
|
|
1484
|
+
)
|
|
1485
|
+
user_id: Mapped[Optional[int]] = mapped_column(
|
|
1486
|
+
ForeignKey("users.id", ondelete="SET NULL"),
|
|
1487
|
+
index=True,
|
|
1488
|
+
nullable=True,
|
|
1489
|
+
)
|
|
1490
|
+
name: Mapped[str]
|
|
1491
|
+
description: Mapped[Optional[str]]
|
|
1492
|
+
experiment: Mapped["Experiment"] = relationship("Experiment", back_populates="experiment_tags")
|
|
1493
|
+
dataset: Mapped["Dataset"] = relationship("Dataset", back_populates="experiment_tags")
|
|
1494
|
+
user: Mapped[Optional["User"]] = relationship("User")
|
|
1495
|
+
|
|
1496
|
+
__table_args__ = (UniqueConstraint("dataset_id", "name"),)
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
class UserRole(HasId):
|
|
1134
1500
|
__tablename__ = "user_roles"
|
|
1135
|
-
name: Mapped[
|
|
1501
|
+
name: Mapped[UserRoleName] = mapped_column(unique=True, index=True)
|
|
1136
1502
|
users: Mapped[list["User"]] = relationship("User", back_populates="role")
|
|
1137
1503
|
|
|
1138
1504
|
|
|
1139
|
-
class User(
|
|
1505
|
+
class User(HasId):
|
|
1140
1506
|
__tablename__ = "users"
|
|
1141
1507
|
user_role_id: Mapped[int] = mapped_column(
|
|
1142
1508
|
ForeignKey("user_roles.id", ondelete="CASCADE"),
|
|
@@ -1231,6 +1597,8 @@ class OAuth2User(User):
|
|
|
1231
1597
|
*,
|
|
1232
1598
|
email: str,
|
|
1233
1599
|
username: str,
|
|
1600
|
+
oauth2_client_id: Optional[str] = None,
|
|
1601
|
+
oauth2_user_id: Optional[str] = None,
|
|
1234
1602
|
user_role_id: Optional[int] = None,
|
|
1235
1603
|
) -> None:
|
|
1236
1604
|
super().__init__(
|
|
@@ -1239,10 +1607,46 @@ class OAuth2User(User):
|
|
|
1239
1607
|
user_role_id=user_role_id,
|
|
1240
1608
|
reset_password=False,
|
|
1241
1609
|
auth_method="OAUTH2",
|
|
1610
|
+
oauth2_client_id=oauth2_client_id,
|
|
1611
|
+
oauth2_user_id=oauth2_user_id,
|
|
1242
1612
|
)
|
|
1243
1613
|
|
|
1244
1614
|
|
|
1245
|
-
|
|
1615
|
+
def LDAPUser(
|
|
1616
|
+
*,
|
|
1617
|
+
email: str,
|
|
1618
|
+
username: str,
|
|
1619
|
+
unique_id: str | None = None,
|
|
1620
|
+
user_role_id: int | None = None,
|
|
1621
|
+
) -> OAuth2User:
|
|
1622
|
+
"""Factory function to create an LDAP user stored as OAuth2User.
|
|
1623
|
+
|
|
1624
|
+
This is a zero-migration approach: LDAP users are stored in the existing
|
|
1625
|
+
OAuth2User table with a special Unicode marker in oauth2_client_id to
|
|
1626
|
+
distinguish them from actual OAuth2 users. This avoids schema changes
|
|
1627
|
+
while allowing LDAP authentication to coexist with OAuth2.
|
|
1628
|
+
|
|
1629
|
+
Args:
|
|
1630
|
+
email: User's email address
|
|
1631
|
+
username: User's display name
|
|
1632
|
+
unique_id: User's LDAP unique ID (stored in oauth2_user_id)
|
|
1633
|
+
user_role_id: Phoenix role ID (ADMIN, MEMBER, VIEWER)
|
|
1634
|
+
|
|
1635
|
+
Returns:
|
|
1636
|
+
OAuth2User instance configured as an LDAP user
|
|
1637
|
+
"""
|
|
1638
|
+
from phoenix.server.ldap import LDAP_CLIENT_ID_MARKER
|
|
1639
|
+
|
|
1640
|
+
return OAuth2User(
|
|
1641
|
+
email=email,
|
|
1642
|
+
username=username,
|
|
1643
|
+
oauth2_client_id=LDAP_CLIENT_ID_MARKER,
|
|
1644
|
+
oauth2_user_id=unique_id,
|
|
1645
|
+
user_role_id=user_role_id,
|
|
1646
|
+
)
|
|
1647
|
+
|
|
1648
|
+
|
|
1649
|
+
class PasswordResetToken(HasId):
|
|
1246
1650
|
__tablename__ = "password_reset_tokens"
|
|
1247
1651
|
user_id: Mapped[int] = mapped_column(
|
|
1248
1652
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
@@ -1251,11 +1655,11 @@ class PasswordResetToken(Base):
|
|
|
1251
1655
|
)
|
|
1252
1656
|
user: Mapped["User"] = relationship("User", back_populates="password_reset_token")
|
|
1253
1657
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1254
|
-
expires_at: Mapped[
|
|
1658
|
+
expires_at: Mapped[datetime] = mapped_column(UtcTimeStamp, nullable=False, index=True)
|
|
1255
1659
|
__table_args__ = (dict(sqlite_autoincrement=True),)
|
|
1256
1660
|
|
|
1257
1661
|
|
|
1258
|
-
class RefreshToken(
|
|
1662
|
+
class RefreshToken(HasId):
|
|
1259
1663
|
__tablename__ = "refresh_tokens"
|
|
1260
1664
|
user_id: Mapped[int] = mapped_column(
|
|
1261
1665
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
@@ -1263,11 +1667,11 @@ class RefreshToken(Base):
|
|
|
1263
1667
|
)
|
|
1264
1668
|
user: Mapped["User"] = relationship("User", back_populates="refresh_tokens")
|
|
1265
1669
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1266
|
-
expires_at: Mapped[
|
|
1670
|
+
expires_at: Mapped[datetime] = mapped_column(UtcTimeStamp, nullable=False, index=True)
|
|
1267
1671
|
__table_args__ = (dict(sqlite_autoincrement=True),)
|
|
1268
1672
|
|
|
1269
1673
|
|
|
1270
|
-
class AccessToken(
|
|
1674
|
+
class AccessToken(HasId):
|
|
1271
1675
|
__tablename__ = "access_tokens"
|
|
1272
1676
|
user_id: Mapped[int] = mapped_column(
|
|
1273
1677
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
@@ -1275,7 +1679,7 @@ class AccessToken(Base):
|
|
|
1275
1679
|
)
|
|
1276
1680
|
user: Mapped["User"] = relationship("User", back_populates="access_tokens")
|
|
1277
1681
|
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
1278
|
-
expires_at: Mapped[
|
|
1682
|
+
expires_at: Mapped[datetime] = mapped_column(UtcTimeStamp, nullable=False, index=True)
|
|
1279
1683
|
refresh_token_id: Mapped[int] = mapped_column(
|
|
1280
1684
|
ForeignKey("refresh_tokens.id", ondelete="CASCADE"),
|
|
1281
1685
|
index=True,
|
|
@@ -1284,7 +1688,7 @@ class AccessToken(Base):
|
|
|
1284
1688
|
__table_args__ = (dict(sqlite_autoincrement=True),)
|
|
1285
1689
|
|
|
1286
1690
|
|
|
1287
|
-
class ApiKey(
|
|
1691
|
+
class ApiKey(HasId):
|
|
1288
1692
|
__tablename__ = "api_keys"
|
|
1289
1693
|
user_id: Mapped[int] = mapped_column(
|
|
1290
1694
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
@@ -1298,9 +1702,86 @@ class ApiKey(Base):
|
|
|
1298
1702
|
__table_args__ = (dict(sqlite_autoincrement=True),)
|
|
1299
1703
|
|
|
1300
1704
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1705
|
+
CostType: TypeAlias = Literal["DEFAULT", "OVERRIDE"]
|
|
1706
|
+
|
|
1707
|
+
|
|
1708
|
+
class GenerativeModel(HasId):
|
|
1709
|
+
__tablename__ = "generative_models"
|
|
1710
|
+
name: Mapped[str] = mapped_column(String, nullable=False)
|
|
1711
|
+
provider: Mapped[str]
|
|
1712
|
+
start_time: Mapped[Optional[datetime]] = mapped_column(UtcTimeStamp)
|
|
1713
|
+
name_pattern: Mapped[re.Pattern[str]] = mapped_column(_RegexStr, nullable=False)
|
|
1714
|
+
is_built_in: Mapped[bool] = mapped_column(
|
|
1715
|
+
Boolean,
|
|
1716
|
+
nullable=False,
|
|
1717
|
+
)
|
|
1718
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
1719
|
+
UtcTimeStamp,
|
|
1720
|
+
server_default=func.now(),
|
|
1721
|
+
)
|
|
1722
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
1723
|
+
UtcTimeStamp,
|
|
1724
|
+
server_default=func.now(),
|
|
1725
|
+
onupdate=func.now(),
|
|
1726
|
+
)
|
|
1727
|
+
deleted_at: Mapped[Optional[datetime]] = mapped_column(UtcTimeStamp)
|
|
1728
|
+
|
|
1729
|
+
token_prices: Mapped[list["TokenPrice"]] = relationship(
|
|
1730
|
+
"TokenPrice",
|
|
1731
|
+
back_populates="model",
|
|
1732
|
+
cascade="all, delete-orphan",
|
|
1733
|
+
uselist=True,
|
|
1734
|
+
)
|
|
1735
|
+
|
|
1736
|
+
__table_args__ = (
|
|
1737
|
+
Index(
|
|
1738
|
+
"ix_generative_models_match_criteria",
|
|
1739
|
+
"name_pattern",
|
|
1740
|
+
"provider",
|
|
1741
|
+
"is_built_in",
|
|
1742
|
+
postgresql_where=sa.text("deleted_at IS NULL"),
|
|
1743
|
+
sqlite_where=sa.text("deleted_at IS NULL"),
|
|
1744
|
+
unique=True,
|
|
1745
|
+
),
|
|
1746
|
+
Index(
|
|
1747
|
+
"ix_generative_models_name_is_built_in",
|
|
1748
|
+
"name",
|
|
1749
|
+
"is_built_in",
|
|
1750
|
+
postgresql_where=sa.text("deleted_at IS NULL"),
|
|
1751
|
+
sqlite_where=sa.text("deleted_at IS NULL"),
|
|
1752
|
+
unique=True,
|
|
1753
|
+
),
|
|
1754
|
+
)
|
|
1755
|
+
|
|
1303
1756
|
|
|
1757
|
+
class TokenPrice(HasId):
|
|
1758
|
+
__tablename__ = "token_prices"
|
|
1759
|
+
model_id: Mapped[int] = mapped_column(
|
|
1760
|
+
ForeignKey("generative_models.id", ondelete="CASCADE"),
|
|
1761
|
+
nullable=False,
|
|
1762
|
+
index=True,
|
|
1763
|
+
)
|
|
1764
|
+
token_type: Mapped[str]
|
|
1765
|
+
is_prompt: Mapped[bool]
|
|
1766
|
+
base_rate: Mapped[float]
|
|
1767
|
+
customization: Mapped[TokenPriceCustomization] = mapped_column(_TokenCustomization)
|
|
1768
|
+
|
|
1769
|
+
model: Mapped["GenerativeModel"] = relationship(
|
|
1770
|
+
"GenerativeModel",
|
|
1771
|
+
back_populates="token_prices",
|
|
1772
|
+
)
|
|
1773
|
+
|
|
1774
|
+
__table_args__ = (
|
|
1775
|
+
UniqueConstraint(
|
|
1776
|
+
"model_id",
|
|
1777
|
+
"token_type",
|
|
1778
|
+
"is_prompt",
|
|
1779
|
+
),
|
|
1780
|
+
)
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
class PromptLabel(HasId):
|
|
1784
|
+
__tablename__ = "prompt_labels"
|
|
1304
1785
|
name: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
|
|
1305
1786
|
description: Mapped[Optional[str]]
|
|
1306
1787
|
color: Mapped[str] = mapped_column(String, nullable=True)
|
|
@@ -1313,9 +1794,8 @@ class PromptLabel(Base):
|
|
|
1313
1794
|
)
|
|
1314
1795
|
|
|
1315
1796
|
|
|
1316
|
-
class Prompt(
|
|
1797
|
+
class Prompt(HasId):
|
|
1317
1798
|
__tablename__ = "prompts"
|
|
1318
|
-
|
|
1319
1799
|
source_prompt_id: Mapped[Optional[int]] = mapped_column(
|
|
1320
1800
|
ForeignKey("prompts.id", ondelete="SET NULL"),
|
|
1321
1801
|
index=True,
|
|
@@ -1351,9 +1831,8 @@ class Prompt(Base):
|
|
|
1351
1831
|
)
|
|
1352
1832
|
|
|
1353
1833
|
|
|
1354
|
-
class PromptPromptLabel(
|
|
1834
|
+
class PromptPromptLabel(HasId):
|
|
1355
1835
|
__tablename__ = "prompts_prompt_labels"
|
|
1356
|
-
|
|
1357
1836
|
prompt_label_id: Mapped[int] = mapped_column(
|
|
1358
1837
|
ForeignKey("prompt_labels.id", ondelete="CASCADE"),
|
|
1359
1838
|
index=True,
|
|
@@ -1373,7 +1852,7 @@ class PromptPromptLabel(Base):
|
|
|
1373
1852
|
__table_args__ = (UniqueConstraint("prompt_label_id", "prompt_id"),)
|
|
1374
1853
|
|
|
1375
1854
|
|
|
1376
|
-
class PromptVersion(
|
|
1855
|
+
class PromptVersion(HasId):
|
|
1377
1856
|
__tablename__ = "prompt_versions"
|
|
1378
1857
|
|
|
1379
1858
|
prompt_id: Mapped[int] = mapped_column(
|
|
@@ -1422,7 +1901,7 @@ class PromptVersion(Base):
|
|
|
1422
1901
|
)
|
|
1423
1902
|
|
|
1424
1903
|
|
|
1425
|
-
class PromptVersionTag(
|
|
1904
|
+
class PromptVersionTag(HasId):
|
|
1426
1905
|
__tablename__ = "prompt_version_tags"
|
|
1427
1906
|
|
|
1428
1907
|
name: Mapped[Identifier] = mapped_column(_Identifier, nullable=False)
|
|
@@ -1451,18 +1930,14 @@ class PromptVersionTag(Base):
|
|
|
1451
1930
|
__table_args__ = (UniqueConstraint("name", "prompt_id"),)
|
|
1452
1931
|
|
|
1453
1932
|
|
|
1454
|
-
class AnnotationConfig(
|
|
1933
|
+
class AnnotationConfig(HasId):
|
|
1455
1934
|
__tablename__ = "annotation_configs"
|
|
1456
|
-
|
|
1457
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
1458
1935
|
name: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
|
1459
1936
|
config: Mapped[AnnotationConfigType] = mapped_column(_AnnotationConfig, nullable=False)
|
|
1460
1937
|
|
|
1461
1938
|
|
|
1462
|
-
class ProjectAnnotationConfig(
|
|
1939
|
+
class ProjectAnnotationConfig(HasId):
|
|
1463
1940
|
__tablename__ = "project_annotation_configs"
|
|
1464
|
-
|
|
1465
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
1466
1941
|
project_id: Mapped[int] = mapped_column(
|
|
1467
1942
|
ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
|
|
1468
1943
|
)
|
|
@@ -1471,3 +1946,141 @@ class ProjectAnnotationConfig(Base):
|
|
|
1471
1946
|
)
|
|
1472
1947
|
|
|
1473
1948
|
__table_args__ = (UniqueConstraint("project_id", "annotation_config_id"),)
|
|
1949
|
+
|
|
1950
|
+
|
|
1951
|
+
class SpanCost(HasId):
|
|
1952
|
+
__tablename__ = "span_costs"
|
|
1953
|
+
|
|
1954
|
+
span_rowid: Mapped[int] = mapped_column(
|
|
1955
|
+
ForeignKey("spans.id", ondelete="CASCADE"),
|
|
1956
|
+
nullable=False,
|
|
1957
|
+
index=True,
|
|
1958
|
+
)
|
|
1959
|
+
trace_rowid: Mapped[int] = mapped_column(
|
|
1960
|
+
ForeignKey("traces.id", ondelete="CASCADE"),
|
|
1961
|
+
nullable=False,
|
|
1962
|
+
index=True,
|
|
1963
|
+
)
|
|
1964
|
+
span_start_time: Mapped[datetime] = mapped_column(
|
|
1965
|
+
UtcTimeStamp,
|
|
1966
|
+
nullable=False,
|
|
1967
|
+
index=True,
|
|
1968
|
+
)
|
|
1969
|
+
model_id: Mapped[Optional[int]] = mapped_column(
|
|
1970
|
+
sa.Integer,
|
|
1971
|
+
ForeignKey(
|
|
1972
|
+
"generative_models.id",
|
|
1973
|
+
ondelete="RESTRICT",
|
|
1974
|
+
),
|
|
1975
|
+
nullable=True,
|
|
1976
|
+
)
|
|
1977
|
+
total_cost: Mapped[Optional[float]]
|
|
1978
|
+
total_tokens: Mapped[Optional[float]]
|
|
1979
|
+
|
|
1980
|
+
@hybrid_property
|
|
1981
|
+
def total_cost_per_token(self) -> Optional[float]:
|
|
1982
|
+
return ((self.total_cost or 0) / self.total_tokens) if self.total_tokens else None
|
|
1983
|
+
|
|
1984
|
+
@total_cost_per_token.inplace.expression
|
|
1985
|
+
@classmethod
|
|
1986
|
+
def _total_cost_per_token_expression(cls) -> ColumnElement[Optional[float]]:
|
|
1987
|
+
return sql.case(
|
|
1988
|
+
(
|
|
1989
|
+
sa.and_(cls.total_tokens.isnot(None), cls.total_tokens != 0),
|
|
1990
|
+
cls.total_cost / cls.total_tokens,
|
|
1991
|
+
)
|
|
1992
|
+
)
|
|
1993
|
+
|
|
1994
|
+
prompt_cost: Mapped[Optional[float]]
|
|
1995
|
+
prompt_tokens: Mapped[Optional[float]]
|
|
1996
|
+
|
|
1997
|
+
@hybrid_property
|
|
1998
|
+
def prompt_cost_per_token(self) -> Optional[float]:
|
|
1999
|
+
return ((self.prompt_cost or 0) / self.prompt_tokens) if self.prompt_tokens else None
|
|
2000
|
+
|
|
2001
|
+
@prompt_cost_per_token.inplace.expression
|
|
2002
|
+
@classmethod
|
|
2003
|
+
def _prompt_cost_per_token_expression(cls) -> ColumnElement[Optional[float]]:
|
|
2004
|
+
return sql.case(
|
|
2005
|
+
(
|
|
2006
|
+
sa.and_(cls.prompt_tokens.isnot(None), cls.prompt_tokens != 0),
|
|
2007
|
+
cls.prompt_cost / cls.prompt_tokens,
|
|
2008
|
+
)
|
|
2009
|
+
)
|
|
2010
|
+
|
|
2011
|
+
completion_cost: Mapped[Optional[float]]
|
|
2012
|
+
completion_tokens: Mapped[Optional[float]]
|
|
2013
|
+
|
|
2014
|
+
@hybrid_property
|
|
2015
|
+
def completion_cost_per_token(self) -> Optional[float]:
|
|
2016
|
+
return (
|
|
2017
|
+
((self.completion_cost or 0) / self.completion_tokens)
|
|
2018
|
+
if self.completion_tokens
|
|
2019
|
+
else None
|
|
2020
|
+
)
|
|
2021
|
+
|
|
2022
|
+
@completion_cost_per_token.inplace.expression
|
|
2023
|
+
@classmethod
|
|
2024
|
+
def _completion_cost_per_token_expression(cls) -> ColumnElement[Optional[float]]:
|
|
2025
|
+
return sql.case(
|
|
2026
|
+
(
|
|
2027
|
+
sa.and_(cls.completion_tokens.isnot(None), cls.completion_tokens != 0),
|
|
2028
|
+
cls.completion_cost / cls.completion_tokens,
|
|
2029
|
+
)
|
|
2030
|
+
)
|
|
2031
|
+
|
|
2032
|
+
span: Mapped["Span"] = relationship("Span", back_populates="span_cost")
|
|
2033
|
+
trace: Mapped["Trace"] = relationship("Trace", back_populates="span_costs")
|
|
2034
|
+
span_cost_details: Mapped[list["SpanCostDetail"]] = relationship(
|
|
2035
|
+
"SpanCostDetail",
|
|
2036
|
+
back_populates="span_cost",
|
|
2037
|
+
cascade="all, delete-orphan",
|
|
2038
|
+
uselist=True,
|
|
2039
|
+
)
|
|
2040
|
+
|
|
2041
|
+
__table_args__ = (
|
|
2042
|
+
Index(
|
|
2043
|
+
"ix_span_costs_model_id_span_start_time",
|
|
2044
|
+
"model_id",
|
|
2045
|
+
"span_start_time",
|
|
2046
|
+
),
|
|
2047
|
+
)
|
|
2048
|
+
|
|
2049
|
+
def append_detail(self, detail: "SpanCostDetail") -> None:
|
|
2050
|
+
self.span_cost_details.append(detail)
|
|
2051
|
+
if cost := detail.cost:
|
|
2052
|
+
if detail.is_prompt:
|
|
2053
|
+
self.prompt_cost = (self.prompt_cost or 0) + cost
|
|
2054
|
+
else:
|
|
2055
|
+
self.completion_cost = (self.completion_cost or 0) + cost
|
|
2056
|
+
self.total_cost = (self.total_cost or 0) + cost
|
|
2057
|
+
if tokens := detail.tokens:
|
|
2058
|
+
if detail.is_prompt:
|
|
2059
|
+
self.prompt_tokens = (self.prompt_tokens or 0) + tokens
|
|
2060
|
+
else:
|
|
2061
|
+
self.completion_tokens = (self.completion_tokens or 0) + tokens
|
|
2062
|
+
self.total_tokens = (self.total_tokens or 0) + tokens
|
|
2063
|
+
|
|
2064
|
+
|
|
2065
|
+
class SpanCostDetail(HasId):
|
|
2066
|
+
__tablename__ = "span_cost_details"
|
|
2067
|
+
span_cost_id: Mapped[int] = mapped_column(
|
|
2068
|
+
ForeignKey("span_costs.id", ondelete="CASCADE"),
|
|
2069
|
+
nullable=False,
|
|
2070
|
+
)
|
|
2071
|
+
token_type: Mapped[str] = mapped_column(index=True)
|
|
2072
|
+
is_prompt: Mapped[bool]
|
|
2073
|
+
|
|
2074
|
+
cost: Mapped[Optional[float]]
|
|
2075
|
+
tokens: Mapped[Optional[float]]
|
|
2076
|
+
cost_per_token: Mapped[Optional[float]]
|
|
2077
|
+
|
|
2078
|
+
span_cost: Mapped["SpanCost"] = relationship("SpanCost", back_populates="span_cost_details")
|
|
2079
|
+
|
|
2080
|
+
__table_args__ = (
|
|
2081
|
+
UniqueConstraint(
|
|
2082
|
+
"span_cost_id",
|
|
2083
|
+
"token_type",
|
|
2084
|
+
"is_prompt",
|
|
2085
|
+
),
|
|
2086
|
+
)
|