arize-phoenix 12.7.0__py3-none-any.whl → 12.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arize-phoenix might be problematic. Click here for more details.

Files changed (19) hide show
  1. {arize_phoenix-12.7.0.dist-info → arize_phoenix-12.8.0.dist-info}/METADATA +1 -1
  2. {arize_phoenix-12.7.0.dist-info → arize_phoenix-12.8.0.dist-info}/RECORD +19 -19
  3. phoenix/server/api/mutations/dataset_label_mutations.py +102 -156
  4. phoenix/server/api/queries.py +16 -4
  5. phoenix/server/cost_tracking/model_cost_manifest.json +2 -2
  6. phoenix/server/static/.vite/manifest.json +43 -43
  7. phoenix/server/static/assets/{components-X_GtZhnz.js → components-Bem6_7MW.js} +380 -423
  8. phoenix/server/static/assets/{index-BCtFqQdo.js → index-NdiXbuNL.js} +12 -12
  9. phoenix/server/static/assets/{pages-Dl8TWyNq.js → pages-CEJgMVKU.js} +761 -765
  10. phoenix/server/static/assets/{vendor-3BvTzoBp.js → vendor-D-csRHGZ.js} +1 -1
  11. phoenix/server/static/assets/{vendor-arizeai-C6_oC0y8.js → vendor-arizeai-BJLCG_Gc.js} +1 -1
  12. phoenix/server/static/assets/{vendor-codemirror-DPnZGAZA.js → vendor-codemirror-Cr963DyP.js} +3 -3
  13. phoenix/server/static/assets/{vendor-recharts-CjgSbsB0.js → vendor-recharts-DgmPLgIp.js} +1 -1
  14. phoenix/server/static/assets/{vendor-shiki-CJyhDG0E.js → vendor-shiki-wYOt1s7u.js} +1 -1
  15. phoenix/version.py +1 -1
  16. {arize_phoenix-12.7.0.dist-info → arize_phoenix-12.8.0.dist-info}/WHEEL +0 -0
  17. {arize_phoenix-12.7.0.dist-info → arize_phoenix-12.8.0.dist-info}/entry_points.txt +0 -0
  18. {arize_phoenix-12.7.0.dist-info → arize_phoenix-12.8.0.dist-info}/licenses/IP_NOTICE +0 -0
  19. {arize_phoenix-12.7.0.dist-info → arize_phoenix-12.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arize-phoenix
3
- Version: 12.7.0
3
+ Version: 12.8.0
4
4
  Summary: AI Observability and Evaluation
5
5
  Project-URL: Documentation, https://arize.com/docs/phoenix/
6
6
  Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
@@ -6,7 +6,7 @@ phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
6
6
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
7
  phoenix/services.py,sha256=ngkyKGVatX3cO2WJdo2hKdaVKP-xJCMvqthvga6kJss,5196
8
8
  phoenix/settings.py,sha256=2kHfT3BNOVd4dAO1bq-syEQbHSG8oX2-7NhOwK2QREk,896
9
- phoenix/version.py,sha256=chhKpeDUFDd7J4CcbTiKgYSFJlEwuOHPuRSBL5Ms-Yw,23
9
+ phoenix/version.py,sha256=gI1fVVoXdBSWPUaSm3faftAdEowgaQCFGYaqnWMq664,23
10
10
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
12
12
  phoenix/core/model.py,sha256=qBFraOtmwCCnWJltKNP18DDG0mULXigytlFsa6YOz6k,4837
@@ -126,7 +126,7 @@ phoenix/server/api/auth_messages.py,sha256=15oqogBNS2BgJLLDecfXz62G9LdiOAEyzPv6j
126
126
  phoenix/server/api/context.py,sha256=MmbVDZuniYDQ9Xw_g371n8_HtqlIUwt_n0Li7FkGZzs,10649
127
127
  phoenix/server/api/exceptions.py,sha256=9gB4nBRNX6k4_fsQZ12yxw6Tw53h_915l06DYK-qkPQ,1442
128
128
  phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
129
- phoenix/server/api/queries.py,sha256=G8FG6xzQhtt6RqVEIX3p55PxHy2QRMSfAGeDjk0gBm8,68287
129
+ phoenix/server/api/queries.py,sha256=SQjsnCcNYve8XnQF0CMJGzSZy60SSZLZsxvR72cdWMk,68824
130
130
  phoenix/server/api/schema.py,sha256=fcs36xQwFF_Qe41_5cWR8wYpDvOrnbcyTeo5WNMbDsA,1702
131
131
  phoenix/server/api/subscriptions.py,sha256=f2o9L04BdMxh6jWHmhdokzu84mifh6u2IF4z6Pt_ZOs,26206
132
132
  phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,833
@@ -262,7 +262,7 @@ phoenix/server/api/mutations/__init__.py,sha256=83V5Ia8ijsajSLo_gzAuHXo4hgDlUGLI
262
262
  phoenix/server/api/mutations/annotation_config_mutations.py,sha256=ZcjWlJ88B_jevoESmOtmO0l-SUEbtAwxwP1KpWTmYqQ,15511
263
263
  phoenix/server/api/mutations/api_key_mutations.py,sha256=3djEPrVOwF7oN3C46WEROWqiBi0EZnue7dTQDAxwBKo,6526
264
264
  phoenix/server/api/mutations/chat_mutations.py,sha256=T9S6IMWVSoFVhkwyXGMg-LGKPcQcKtKb7Y4nsDbRr_0,26020
265
- phoenix/server/api/mutations/dataset_label_mutations.py,sha256=Paq1zsWyrAXTcfPvX5Bn53LDt1rIIrJtAo5XlRP-0Ts,11660
265
+ phoenix/server/api/mutations/dataset_label_mutations.py,sha256=qT5JoV4gtJwctzxwGsqyzyhMUQhSrIhAeRQ5J7-FE20,9524
266
266
  phoenix/server/api/mutations/dataset_mutations.py,sha256=BsBW2epV3gtgnO2fn3lJP9uX2qOZEY6gy0UW1UEgpUA,28137
267
267
  phoenix/server/api/mutations/dataset_split_mutations.py,sha256=Mbmr6LyqqoaLfB4rwMrx2V0GICHlcJ34hyj6c1k7tvk,17415
268
268
  phoenix/server/api/mutations/experiment_mutations.py,sha256=_fWkKl3VEMF4w885vIuh8zjH9SOhlXc4QaHzGLgBnuY,3214
@@ -402,7 +402,7 @@ phoenix/server/cost_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
402
402
  phoenix/server/cost_tracking/cost_details_calculator.py,sha256=Tt0YcuLhgPuXKWJemWVmYQfG0xQUvH4VziIj6KcDnoA,8945
403
403
  phoenix/server/cost_tracking/cost_model_lookup.py,sha256=jhtVdnQBzrTUHeOGPWgOebk-Io5hpJ1vAgWOu8ojeJ4,6801
404
404
  phoenix/server/cost_tracking/helpers.py,sha256=Pk6ECjnYreTxrldtRwxnwFcxIPVsvDq_yAwDA_spkOc,2122
405
- phoenix/server/cost_tracking/model_cost_manifest.json,sha256=d8r3YEhCH1PxX8ebla-k0JAKk6e2cjkxWQ0ybQ2zrC4,71781
405
+ phoenix/server/cost_tracking/model_cost_manifest.json,sha256=uRmDJ7GZgNSV8ehaEbFzMtn4g4YkUGPHAmRGsPWCRNg,71778
406
406
  phoenix/server/cost_tracking/regex_specificity.py,sha256=9kqWuQ68C-hlwW25hr7BhFlRt5y2Nnpy0Ax3n9UN6Xk,11622
407
407
  phoenix/server/cost_tracking/token_cost_calculator.py,sha256=2JEZnvusx2-xbhp8krp9EarjWuyGH2KO4e-ZwJX-K0s,1598
408
408
  phoenix/server/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -431,16 +431,16 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
431
431
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
432
432
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
433
433
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
434
- phoenix/server/static/.vite/manifest.json,sha256=o_7W7W2yi7eRf9EUNoLT4aOlcRodtT7OlfNWo9agUa0,2328
435
- phoenix/server/static/assets/components-X_GtZhnz.js,sha256=qKnZEKJCeNl28Me3cqs1jEPM2OCJQEaXd-vM_qJQ7hQ,758722
436
- phoenix/server/static/assets/index-BCtFqQdo.js,sha256=LhoeZWDjS_vlgvwY4nz8c8Ogkn19zwr94vJzK0evXXk,63675
437
- phoenix/server/static/assets/pages-Dl8TWyNq.js,sha256=W5kL05Jv_gyf7ExeJ9zX5YicL2u8RsuBkUqRW22aPiU,1352914
438
- phoenix/server/static/assets/vendor-3BvTzoBp.js,sha256=TTjoWR-GrJf3kUBuRWOKsoL66pKco299wKSb7lgaKLA,2676807
434
+ phoenix/server/static/.vite/manifest.json,sha256=xZi8Y0krYRlTEV7AKGciIFWUty9Nx_klwu5ZflV0Biw,2328
435
+ phoenix/server/static/assets/components-Bem6_7MW.js,sha256=A8bCIl-w4LKbsQZbVLMVu2cD3uQQpAs4DdvOo6EFZY4,751728
436
+ phoenix/server/static/assets/index-NdiXbuNL.js,sha256=SPndC2GMCbo-fmLgKRWW4kJQWvfJkbON2OxmaVqfkrQ,63699
437
+ phoenix/server/static/assets/pages-CEJgMVKU.js,sha256=NGL0M3R08Za7qwAQJE3Ob_PqOkbPhclP4W75CmzimmQ,1357599
439
438
  phoenix/server/static/assets/vendor-BGzfc4EU.css,sha256=Nx5Lmx-bqYR7nsO_O4kEBcrJ8cwknWjZ6seHN3_s4UQ,3171
440
- phoenix/server/static/assets/vendor-arizeai-C6_oC0y8.js,sha256=meOakkfYcA4TSYjxbobysSn5Z8YdOw2EaBZP3iC977g,61555
441
- phoenix/server/static/assets/vendor-codemirror-DPnZGAZA.js,sha256=Ry5RV48MqAg_Q1PK0-URAbyinGajuQ728O71QBVD-u8,413334
442
- phoenix/server/static/assets/vendor-recharts-CjgSbsB0.js,sha256=lnJ2ZyzB_LTd0pLIp2fohK9v9UrzMLeY1TlzDIpJQok,231652
443
- phoenix/server/static/assets/vendor-shiki-CJyhDG0E.js,sha256=Aia7kJ1a_hjZkazx9ePQjwgI_eyjq-7geCiGHJDDnXw,305160
439
+ phoenix/server/static/assets/vendor-D-csRHGZ.js,sha256=uYz5BUvg6gsB97JV0OZFu7rDn40gBkL3WYmrcQW5kgk,2676807
440
+ phoenix/server/static/assets/vendor-arizeai-BJLCG_Gc.js,sha256=x0nzGpalauyWr0XeuvQBfvF6lkvs0Limu50IfT6LWeI,61555
441
+ phoenix/server/static/assets/vendor-codemirror-Cr963DyP.js,sha256=1an8AWTBNoLFizv2VpW5c6t77gbG_YJB_lF93qyUKiQ,413334
442
+ phoenix/server/static/assets/vendor-recharts-DgmPLgIp.js,sha256=Lo0yw1RW2uvp61KL4-uFd67ZTRpzCND7PRKNVFPmqww,231652
443
+ phoenix/server/static/assets/vendor-shiki-wYOt1s7u.js,sha256=YoaKapH5k--O3pS9E1kUVfLxCFGKryoo9dDr2JjiXIs,305160
444
444
  phoenix/server/static/assets/vendor-three-BtCyLs1w.js,sha256=E6e1HbskKn61fWQPWmiZiPXXGHfZYn-30v0nofpDaqo,704132
445
445
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
446
446
  phoenix/server/templates/index.html,sha256=_iKIyXEDDr5cTTnrUCjCd617U6Alc1k-IXtdKSt8g14,7215
@@ -480,9 +480,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
480
480
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
481
481
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
482
482
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
483
- arize_phoenix-12.7.0.dist-info/METADATA,sha256=LnGkrf4RgexzjMydbt9ppbNvDmiTCjQd_f3xtH4O2YQ,35327
484
- arize_phoenix-12.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
485
- arize_phoenix-12.7.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
486
- arize_phoenix-12.7.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
487
- arize_phoenix-12.7.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
488
- arize_phoenix-12.7.0.dist-info/RECORD,,
483
+ arize_phoenix-12.8.0.dist-info/METADATA,sha256=xRbO3Khh91zENiRVuhmmIJZywsa_498EzAAFN3WB9wY,35327
484
+ arize_phoenix-12.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
485
+ arize_phoenix-12.8.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
486
+ arize_phoenix-12.8.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
487
+ arize_phoenix-12.8.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
488
+ arize_phoenix-12.8.0.dist-info/RECORD,,
@@ -4,6 +4,8 @@ import sqlalchemy
4
4
  import strawberry
5
5
  from sqlalchemy import delete, select
6
6
  from sqlalchemy.exc import IntegrityError as PostgreSQLIntegrityError
7
+ from sqlalchemy.orm import joinedload
8
+ from sqlalchemy.sql import tuple_
7
9
  from sqlean.dbapi2 import IntegrityError as SQLiteIntegrityError # type: ignore[import-untyped]
8
10
  from strawberry import UNSET
9
11
  from strawberry.relay.types import GlobalID
@@ -14,7 +16,7 @@ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
14
16
  from phoenix.server.api.context import Context
15
17
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
16
18
  from phoenix.server.api.queries import Query
17
- from phoenix.server.api.types.Dataset import Dataset
19
+ from phoenix.server.api.types.Dataset import Dataset, to_gql_dataset
18
20
  from phoenix.server.api.types.DatasetLabel import DatasetLabel, to_gql_dataset_label
19
21
  from phoenix.server.api.types.node import from_global_id_with_expected_type
20
22
 
@@ -24,11 +26,13 @@ class CreateDatasetLabelInput:
24
26
  name: str
25
27
  description: Optional[str] = UNSET
26
28
  color: str
29
+ dataset_ids: Optional[list[GlobalID]] = UNSET
27
30
 
28
31
 
29
32
  @strawberry.type
30
33
  class CreateDatasetLabelMutationPayload:
31
34
  dataset_label: DatasetLabel
35
+ datasets: list[Dataset]
32
36
 
33
37
 
34
38
  @strawberry.input
@@ -41,39 +45,16 @@ class DeleteDatasetLabelsMutationPayload:
41
45
  dataset_labels: list[DatasetLabel]
42
46
 
43
47
 
44
- @strawberry.input
45
- class UpdateDatasetLabelInput:
46
- dataset_label_id: GlobalID
47
- name: str
48
- description: Optional[str] = None
49
- color: str
50
-
51
-
52
- @strawberry.type
53
- class UpdateDatasetLabelMutationPayload:
54
- dataset_label: DatasetLabel
55
-
56
-
57
48
  @strawberry.input
58
49
  class SetDatasetLabelsInput:
50
+ dataset_id: GlobalID
59
51
  dataset_label_ids: list[GlobalID]
60
- dataset_ids: list[GlobalID]
61
52
 
62
53
 
63
54
  @strawberry.type
64
55
  class SetDatasetLabelsMutationPayload:
65
- query: "Query"
66
-
67
-
68
- @strawberry.input
69
- class UnsetDatasetLabelsInput:
70
- dataset_label_ids: list[GlobalID]
71
- dataset_ids: list[GlobalID]
72
-
73
-
74
- @strawberry.type
75
- class UnsetDatasetLabelsMutationPayload:
76
- query: "Query"
56
+ query: Query
57
+ dataset: Dataset
77
58
 
78
59
 
79
60
  @strawberry.type
@@ -87,50 +68,53 @@ class DatasetLabelMutationMixin:
87
68
  name = input.name
88
69
  description = input.description
89
70
  color = input.color
71
+ dataset_rowids: dict[
72
+ int, None
73
+ ] = {} # use dictionary to de-duplicate while preserving order
74
+ if input.dataset_ids:
75
+ for dataset_id in input.dataset_ids:
76
+ try:
77
+ dataset_rowid = from_global_id_with_expected_type(dataset_id, Dataset.__name__)
78
+ except ValueError:
79
+ raise BadRequest(f"Invalid dataset ID: {dataset_id}")
80
+ dataset_rowids[dataset_rowid] = None
81
+
90
82
  async with info.context.db() as session:
91
83
  dataset_label_orm = models.DatasetLabel(name=name, description=description, color=color)
92
84
  session.add(dataset_label_orm)
93
85
  try:
94
- await session.commit()
86
+ await session.flush()
95
87
  except (PostgreSQLIntegrityError, SQLiteIntegrityError):
96
88
  raise Conflict(f"A dataset label named '{name}' already exists")
97
89
  except sqlalchemy.exc.StatementError as error:
98
90
  raise BadRequest(str(error.orig))
99
- return CreateDatasetLabelMutationPayload(
100
- dataset_label=to_gql_dataset_label(dataset_label_orm)
101
- )
102
-
103
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
104
- async def update_dataset_label(
105
- self, info: Info[Context, None], input: UpdateDatasetLabelInput
106
- ) -> UpdateDatasetLabelMutationPayload:
107
- if not input.name or not input.name.strip():
108
- raise BadRequest("Dataset label name cannot be empty")
109
-
110
- try:
111
- dataset_label_id = from_global_id_with_expected_type(
112
- input.dataset_label_id, DatasetLabel.__name__
113
- )
114
- except ValueError:
115
- raise BadRequest(f"Invalid dataset label ID: {input.dataset_label_id}")
116
-
117
- async with info.context.db() as session:
118
- dataset_label_orm = await session.get(models.DatasetLabel, dataset_label_id)
119
- if not dataset_label_orm:
120
- raise NotFound(f"DatasetLabel with ID {input.dataset_label_id} not found")
121
-
122
- dataset_label_orm.name = input.name.strip()
123
- dataset_label_orm.description = input.description
124
- dataset_label_orm.color = input.color.strip()
125
91
 
126
- try:
92
+ datasets_by_id: dict[int, models.Dataset] = {}
93
+ if dataset_rowids:
94
+ datasets_by_id = {
95
+ dataset.id: dataset
96
+ for dataset in await session.scalars(
97
+ select(models.Dataset).where(models.Dataset.id.in_(dataset_rowids.keys()))
98
+ )
99
+ }
100
+ if len(datasets_by_id) < len(dataset_rowids):
101
+ raise NotFound("One or more datasets not found")
102
+ session.add_all(
103
+ [
104
+ models.DatasetsDatasetLabel(
105
+ dataset_id=dataset_rowid,
106
+ dataset_label_id=dataset_label_orm.id,
107
+ )
108
+ for dataset_rowid in dataset_rowids
109
+ ]
110
+ )
127
111
  await session.commit()
128
- except (PostgreSQLIntegrityError, SQLiteIntegrityError):
129
- raise Conflict(f"A dataset label named '{input.name}' already exists")
130
- except sqlalchemy.exc.StatementError as error:
131
- raise BadRequest(str(error.orig))
132
- return UpdateDatasetLabelMutationPayload(
133
- dataset_label=to_gql_dataset_label(dataset_label_orm)
112
+
113
+ return CreateDatasetLabelMutationPayload(
114
+ dataset_label=to_gql_dataset_label(dataset_label_orm),
115
+ datasets=[
116
+ to_gql_dataset(datasets_by_id[dataset_rowid]) for dataset_rowid in dataset_rowids
117
+ ],
134
118
  )
135
119
 
136
120
  @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
@@ -170,122 +154,84 @@ class DatasetLabelMutationMixin:
170
154
  async def set_dataset_labels(
171
155
  self, info: Info[Context, None], input: SetDatasetLabelsInput
172
156
  ) -> SetDatasetLabelsMutationPayload:
173
- if not input.dataset_ids:
174
- raise BadRequest("No datasets provided.")
175
- if not input.dataset_label_ids:
176
- raise BadRequest("No dataset labels provided.")
177
-
178
- unique_dataset_rowids: set[int] = set()
179
- for dataset_gid in input.dataset_ids:
180
- try:
181
- dataset_rowid = from_global_id_with_expected_type(dataset_gid, Dataset.__name__)
182
- except ValueError:
183
- raise BadRequest(f"Invalid dataset ID: {dataset_gid}")
184
- unique_dataset_rowids.add(dataset_rowid)
185
- dataset_rowids = list(unique_dataset_rowids)
157
+ try:
158
+ dataset_id = from_global_id_with_expected_type(input.dataset_id, Dataset.__name__)
159
+ except ValueError:
160
+ raise BadRequest(f"Invalid dataset ID: {input.dataset_id}")
186
161
 
187
- unique_dataset_label_rowids: set[int] = set()
162
+ dataset_label_ids: dict[
163
+ int, None
164
+ ] = {} # use dictionary to de-duplicate while preserving order
188
165
  for dataset_label_gid in input.dataset_label_ids:
189
166
  try:
190
- dataset_label_rowid = from_global_id_with_expected_type(
167
+ dataset_label_id = from_global_id_with_expected_type(
191
168
  dataset_label_gid, DatasetLabel.__name__
192
169
  )
193
170
  except ValueError:
194
171
  raise BadRequest(f"Invalid dataset label ID: {dataset_label_gid}")
195
- unique_dataset_label_rowids.add(dataset_label_rowid)
196
- dataset_label_rowids = list(unique_dataset_label_rowids)
172
+ dataset_label_ids[dataset_label_id] = None
197
173
 
198
174
  async with info.context.db() as session:
199
- existing_dataset_ids = (
200
- await session.scalars(
201
- select(models.Dataset.id).where(models.Dataset.id.in_(dataset_rowids))
202
- )
203
- ).all()
204
- if len(existing_dataset_ids) != len(dataset_rowids):
205
- raise NotFound("One or more datasets not found")
175
+ dataset = await session.scalar(
176
+ select(models.Dataset)
177
+ .where(models.Dataset.id == dataset_id)
178
+ .options(joinedload(models.Dataset.datasets_dataset_labels))
179
+ )
206
180
 
207
- existing_dataset_label_ids = (
181
+ if not dataset:
182
+ raise NotFound(f"Dataset with ID {input.dataset_id} not found")
183
+
184
+ existing_label_ids = (
208
185
  await session.scalars(
209
186
  select(models.DatasetLabel.id).where(
210
- models.DatasetLabel.id.in_(dataset_label_rowids)
187
+ models.DatasetLabel.id.in_(dataset_label_ids.keys())
211
188
  )
212
189
  )
213
190
  ).all()
214
- if len(existing_dataset_label_ids) != len(dataset_label_rowids):
191
+ if len(existing_label_ids) != len(dataset_label_ids):
215
192
  raise NotFound("One or more dataset labels not found")
216
193
 
217
- existing_dataset_label_keys = await session.execute(
218
- select(
219
- models.DatasetsDatasetLabel.dataset_id,
220
- models.DatasetsDatasetLabel.dataset_label_id,
221
- ).where(
222
- models.DatasetsDatasetLabel.dataset_id.in_(dataset_rowids)
223
- & models.DatasetsDatasetLabel.dataset_label_id.in_(dataset_label_rowids)
224
- )
225
- )
226
- unique_dataset_label_keys = set(existing_dataset_label_keys.all())
194
+ previously_applied_dataset_label_ids = {
195
+ dataset_dataset_label.dataset_label_id
196
+ for dataset_dataset_label in dataset.datasets_dataset_labels
197
+ }
227
198
 
228
- datasets_dataset_labels = []
229
- for dataset_rowid in dataset_rowids:
230
- for dataset_label_rowid in dataset_label_rowids:
231
- if (dataset_rowid, dataset_label_rowid) in unique_dataset_label_keys:
232
- continue
233
- datasets_dataset_labels.append(
234
- models.DatasetsDatasetLabel(
235
- dataset_id=dataset_rowid,
236
- dataset_label_id=dataset_label_rowid,
199
+ datasets_dataset_labels_to_add = [
200
+ models.DatasetsDatasetLabel(
201
+ dataset_id=dataset_id,
202
+ dataset_label_id=dataset_label_id,
203
+ )
204
+ for dataset_label_id in dataset_label_ids
205
+ if dataset_label_id not in previously_applied_dataset_label_ids
206
+ ]
207
+ if datasets_dataset_labels_to_add:
208
+ session.add_all(datasets_dataset_labels_to_add)
209
+ await session.flush()
210
+
211
+ datasets_dataset_labels_to_delete = [
212
+ dataset_dataset_label
213
+ for dataset_dataset_label in dataset.datasets_dataset_labels
214
+ if dataset_dataset_label.dataset_label_id not in dataset_label_ids
215
+ ]
216
+ if datasets_dataset_labels_to_delete:
217
+ await session.execute(
218
+ delete(models.DatasetsDatasetLabel).where(
219
+ tuple_(
220
+ models.DatasetsDatasetLabel.dataset_id,
221
+ models.DatasetsDatasetLabel.dataset_label_id,
222
+ ).in_(
223
+ [
224
+ (
225
+ datasets_dataset_labels.dataset_id,
226
+ datasets_dataset_labels.dataset_label_id,
227
+ )
228
+ for datasets_dataset_labels in datasets_dataset_labels_to_delete
229
+ ]
237
230
  )
238
231
  )
239
- session.add_all(datasets_dataset_labels)
240
-
241
- if datasets_dataset_labels:
242
- try:
243
- await session.commit()
244
- except (PostgreSQLIntegrityError, SQLiteIntegrityError) as e:
245
- raise Conflict("Failed to add dataset labels to datasets.") from e
246
-
247
- return SetDatasetLabelsMutationPayload(
248
- query=Query(),
249
- )
250
-
251
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
252
- async def unset_dataset_labels(
253
- self, info: Info[Context, None], input: UnsetDatasetLabelsInput
254
- ) -> UnsetDatasetLabelsMutationPayload:
255
- if not input.dataset_ids:
256
- raise BadRequest("No datasets provided.")
257
- if not input.dataset_label_ids:
258
- raise BadRequest("No dataset labels provided.")
259
-
260
- unique_dataset_rowids: set[int] = set()
261
- for dataset_gid in input.dataset_ids:
262
- try:
263
- dataset_rowid = from_global_id_with_expected_type(dataset_gid, Dataset.__name__)
264
- except ValueError:
265
- raise BadRequest(f"Invalid dataset ID: {dataset_gid}")
266
- unique_dataset_rowids.add(dataset_rowid)
267
- dataset_rowids = list(unique_dataset_rowids)
268
-
269
- unique_dataset_label_rowids: set[int] = set()
270
- for dataset_label_gid in input.dataset_label_ids:
271
- try:
272
- dataset_label_rowid = from_global_id_with_expected_type(
273
- dataset_label_gid, DatasetLabel.__name__
274
- )
275
- except ValueError:
276
- raise BadRequest(f"Invalid dataset label ID: {dataset_label_gid}")
277
- unique_dataset_label_rowids.add(dataset_label_rowid)
278
- dataset_label_rowids = list(unique_dataset_label_rowids)
279
-
280
- async with info.context.db() as session:
281
- await session.execute(
282
- delete(models.DatasetsDatasetLabel).where(
283
- models.DatasetsDatasetLabel.dataset_id.in_(dataset_rowids)
284
- & models.DatasetsDatasetLabel.dataset_label_id.in_(dataset_label_rowids)
285
232
  )
286
- )
287
- await session.commit()
288
233
 
289
- return UnsetDatasetLabelsMutationPayload(
234
+ return SetDatasetLabelsMutationPayload(
235
+ dataset=to_gql_dataset(dataset),
290
236
  query=Query(),
291
237
  )
@@ -188,7 +188,17 @@ class Query:
188
188
  async def generative_models(
189
189
  self,
190
190
  info: Info[Context, None],
191
- ) -> list[GenerativeModel]:
191
+ first: Optional[int] = 50,
192
+ last: Optional[int] = UNSET,
193
+ after: Optional[CursorString] = UNSET,
194
+ before: Optional[CursorString] = UNSET,
195
+ ) -> Connection[GenerativeModel]:
196
+ args = ConnectionArgs(
197
+ first=first,
198
+ after=after if isinstance(after, CursorString) else None,
199
+ last=last,
200
+ before=before if isinstance(before, CursorString) else None,
201
+ )
192
202
  async with info.context.db() as session:
193
203
  result = await session.scalars(
194
204
  select(models.GenerativeModel)
@@ -200,8 +210,8 @@ class Query:
200
210
  )
201
211
  .options(joinedload(models.GenerativeModel.token_prices))
202
212
  )
203
-
204
- return [to_gql_generative_model(model) for model in result.unique()]
213
+ data = [to_gql_generative_model(model) for model in result.unique()]
214
+ return connection_from_list(data=data, args=args)
205
215
 
206
216
  @strawberry.field
207
217
  async def playground_models(self, input: Optional[ModelsInput] = None) -> list[PlaygroundModel]:
@@ -1191,7 +1201,9 @@ class Query:
1191
1201
  before=before if isinstance(before, CursorString) else None,
1192
1202
  )
1193
1203
  async with info.context.db() as session:
1194
- dataset_labels = await session.scalars(select(models.DatasetLabel))
1204
+ dataset_labels = await session.scalars(
1205
+ select(models.DatasetLabel).order_by(models.DatasetLabel.name.asc())
1206
+ )
1195
1207
  data = [to_gql_dataset_label(dataset_label) for dataset_label in dataset_labels]
1196
1208
  return connection_from_list(data=data, args=args)
1197
1209
 
@@ -784,7 +784,7 @@
784
784
  "token_type": "output"
785
785
  },
786
786
  {
787
- "base_rate": 7.5e-8,
787
+ "base_rate": 3e-8,
788
788
  "is_prompt": true,
789
789
  "token_type": "cache_read"
790
790
  },
@@ -1027,7 +1027,7 @@
1027
1027
  "token_type": "output"
1028
1028
  },
1029
1029
  {
1030
- "base_rate": 3.125e-7,
1030
+ "base_rate": 1.25e-7,
1031
1031
  "is_prompt": true,
1032
1032
  "token_type": "cache_read"
1033
1033
  }
@@ -1,28 +1,32 @@
1
1
  {
2
- "_components-X_GtZhnz.js": {
3
- "file": "assets/components-X_GtZhnz.js",
2
+ "_components-Bem6_7MW.js": {
3
+ "file": "assets/components-Bem6_7MW.js",
4
4
  "name": "components",
5
5
  "imports": [
6
- "_vendor-3BvTzoBp.js",
7
- "_pages-Dl8TWyNq.js",
8
- "_vendor-arizeai-C6_oC0y8.js",
9
- "_vendor-codemirror-DPnZGAZA.js",
6
+ "_vendor-D-csRHGZ.js",
7
+ "_pages-CEJgMVKU.js",
8
+ "_vendor-arizeai-BJLCG_Gc.js",
9
+ "_vendor-codemirror-Cr963DyP.js",
10
10
  "_vendor-three-BtCyLs1w.js"
11
11
  ]
12
12
  },
13
- "_pages-Dl8TWyNq.js": {
14
- "file": "assets/pages-Dl8TWyNq.js",
13
+ "_pages-CEJgMVKU.js": {
14
+ "file": "assets/pages-CEJgMVKU.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
- "_vendor-3BvTzoBp.js",
18
- "_components-X_GtZhnz.js",
19
- "_vendor-arizeai-C6_oC0y8.js",
20
- "_vendor-codemirror-DPnZGAZA.js",
21
- "_vendor-recharts-CjgSbsB0.js"
17
+ "_vendor-D-csRHGZ.js",
18
+ "_components-Bem6_7MW.js",
19
+ "_vendor-arizeai-BJLCG_Gc.js",
20
+ "_vendor-codemirror-Cr963DyP.js",
21
+ "_vendor-recharts-DgmPLgIp.js"
22
22
  ]
23
23
  },
24
- "_vendor-3BvTzoBp.js": {
25
- "file": "assets/vendor-3BvTzoBp.js",
24
+ "_vendor-BGzfc4EU.css": {
25
+ "file": "assets/vendor-BGzfc4EU.css",
26
+ "src": "_vendor-BGzfc4EU.css"
27
+ },
28
+ "_vendor-D-csRHGZ.js": {
29
+ "file": "assets/vendor-D-csRHGZ.js",
26
30
  "name": "vendor",
27
31
  "imports": [
28
32
  "_vendor-three-BtCyLs1w.js"
@@ -31,43 +35,39 @@
31
35
  "assets/vendor-BGzfc4EU.css"
32
36
  ]
33
37
  },
34
- "_vendor-BGzfc4EU.css": {
35
- "file": "assets/vendor-BGzfc4EU.css",
36
- "src": "_vendor-BGzfc4EU.css"
37
- },
38
- "_vendor-arizeai-C6_oC0y8.js": {
39
- "file": "assets/vendor-arizeai-C6_oC0y8.js",
38
+ "_vendor-arizeai-BJLCG_Gc.js": {
39
+ "file": "assets/vendor-arizeai-BJLCG_Gc.js",
40
40
  "name": "vendor-arizeai",
41
41
  "imports": [
42
- "_vendor-3BvTzoBp.js"
42
+ "_vendor-D-csRHGZ.js"
43
43
  ]
44
44
  },
45
- "_vendor-codemirror-DPnZGAZA.js": {
46
- "file": "assets/vendor-codemirror-DPnZGAZA.js",
45
+ "_vendor-codemirror-Cr963DyP.js": {
46
+ "file": "assets/vendor-codemirror-Cr963DyP.js",
47
47
  "name": "vendor-codemirror",
48
48
  "imports": [
49
- "_vendor-3BvTzoBp.js",
50
- "_vendor-shiki-CJyhDG0E.js"
49
+ "_vendor-D-csRHGZ.js",
50
+ "_vendor-shiki-wYOt1s7u.js"
51
51
  ],
52
52
  "dynamicImports": [
53
- "_vendor-shiki-CJyhDG0E.js",
54
- "_vendor-shiki-CJyhDG0E.js",
55
- "_vendor-shiki-CJyhDG0E.js"
53
+ "_vendor-shiki-wYOt1s7u.js",
54
+ "_vendor-shiki-wYOt1s7u.js",
55
+ "_vendor-shiki-wYOt1s7u.js"
56
56
  ]
57
57
  },
58
- "_vendor-recharts-CjgSbsB0.js": {
59
- "file": "assets/vendor-recharts-CjgSbsB0.js",
58
+ "_vendor-recharts-DgmPLgIp.js": {
59
+ "file": "assets/vendor-recharts-DgmPLgIp.js",
60
60
  "name": "vendor-recharts",
61
61
  "imports": [
62
- "_vendor-3BvTzoBp.js"
62
+ "_vendor-D-csRHGZ.js"
63
63
  ]
64
64
  },
65
- "_vendor-shiki-CJyhDG0E.js": {
66
- "file": "assets/vendor-shiki-CJyhDG0E.js",
65
+ "_vendor-shiki-wYOt1s7u.js": {
66
+ "file": "assets/vendor-shiki-wYOt1s7u.js",
67
67
  "name": "vendor-shiki",
68
68
  "isDynamicEntry": true,
69
69
  "imports": [
70
- "_vendor-3BvTzoBp.js"
70
+ "_vendor-D-csRHGZ.js"
71
71
  ]
72
72
  },
73
73
  "_vendor-three-BtCyLs1w.js": {
@@ -75,19 +75,19 @@
75
75
  "name": "vendor-three"
76
76
  },
77
77
  "index.tsx": {
78
- "file": "assets/index-BCtFqQdo.js",
78
+ "file": "assets/index-NdiXbuNL.js",
79
79
  "name": "index",
80
80
  "src": "index.tsx",
81
81
  "isEntry": true,
82
82
  "imports": [
83
- "_vendor-3BvTzoBp.js",
84
- "_vendor-arizeai-C6_oC0y8.js",
85
- "_pages-Dl8TWyNq.js",
86
- "_components-X_GtZhnz.js",
83
+ "_vendor-D-csRHGZ.js",
84
+ "_vendor-arizeai-BJLCG_Gc.js",
85
+ "_pages-CEJgMVKU.js",
86
+ "_components-Bem6_7MW.js",
87
87
  "_vendor-three-BtCyLs1w.js",
88
- "_vendor-codemirror-DPnZGAZA.js",
89
- "_vendor-shiki-CJyhDG0E.js",
90
- "_vendor-recharts-CjgSbsB0.js"
88
+ "_vendor-codemirror-Cr963DyP.js",
89
+ "_vendor-shiki-wYOt1s7u.js",
90
+ "_vendor-recharts-DgmPLgIp.js"
91
91
  ]
92
92
  }
93
93
  }