arize-phoenix 12.4.0__py3-none-any.whl → 12.5.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 (59) hide show
  1. {arize_phoenix-12.4.0.dist-info → arize_phoenix-12.5.0.dist-info}/METADATA +1 -1
  2. {arize_phoenix-12.4.0.dist-info → arize_phoenix-12.5.0.dist-info}/RECORD +59 -58
  3. phoenix/auth.py +8 -2
  4. phoenix/db/models.py +3 -3
  5. phoenix/server/api/auth.py +9 -0
  6. phoenix/server/api/context.py +2 -0
  7. phoenix/server/api/dataloaders/__init__.py +2 -0
  8. phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
  9. phoenix/server/api/input_types/ProjectSessionSort.py +158 -1
  10. phoenix/server/api/input_types/SpanSort.py +2 -1
  11. phoenix/server/api/input_types/UserRoleInput.py +1 -0
  12. phoenix/server/api/mutations/annotation_config_mutations.py +6 -6
  13. phoenix/server/api/mutations/api_key_mutations.py +13 -5
  14. phoenix/server/api/mutations/chat_mutations.py +3 -3
  15. phoenix/server/api/mutations/dataset_label_mutations.py +6 -6
  16. phoenix/server/api/mutations/dataset_mutations.py +8 -8
  17. phoenix/server/api/mutations/dataset_split_mutations.py +7 -7
  18. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  19. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  20. phoenix/server/api/mutations/model_mutations.py +4 -4
  21. phoenix/server/api/mutations/project_mutations.py +4 -4
  22. phoenix/server/api/mutations/project_session_annotations_mutations.py +4 -4
  23. phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
  24. phoenix/server/api/mutations/prompt_label_mutations.py +7 -7
  25. phoenix/server/api/mutations/prompt_mutations.py +7 -7
  26. phoenix/server/api/mutations/prompt_version_tag_mutations.py +3 -3
  27. phoenix/server/api/mutations/span_annotations_mutations.py +5 -5
  28. phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
  29. phoenix/server/api/mutations/trace_mutations.py +3 -3
  30. phoenix/server/api/mutations/user_mutations.py +8 -5
  31. phoenix/server/api/routers/auth.py +2 -2
  32. phoenix/server/api/routers/v1/__init__.py +16 -1
  33. phoenix/server/api/routers/v1/annotation_configs.py +7 -1
  34. phoenix/server/api/routers/v1/datasets.py +48 -8
  35. phoenix/server/api/routers/v1/experiment_runs.py +7 -1
  36. phoenix/server/api/routers/v1/experiments.py +41 -5
  37. phoenix/server/api/routers/v1/projects.py +3 -31
  38. phoenix/server/api/routers/v1/users.py +0 -7
  39. phoenix/server/api/subscriptions.py +3 -3
  40. phoenix/server/api/types/Dataset.py +95 -6
  41. phoenix/server/api/types/Project.py +24 -68
  42. phoenix/server/app.py +2 -0
  43. phoenix/server/authorization.py +3 -1
  44. phoenix/server/bearer_auth.py +9 -0
  45. phoenix/server/jwt_store.py +8 -6
  46. phoenix/server/static/.vite/manifest.json +39 -39
  47. phoenix/server/static/assets/{components-BvsExS75.js → components-cwdYEs7B.js} +501 -394
  48. phoenix/server/static/assets/{index-iq8WDxat.js → index-Dc0vD1Rn.js} +1 -1
  49. phoenix/server/static/assets/{pages-Ckg4SLQ9.js → pages-BDkB3a_a.js} +577 -533
  50. phoenix/server/static/assets/{vendor-D2eEI-6h.js → vendor-Ce6GTAin.js} +1 -1
  51. phoenix/server/static/assets/{vendor-arizeai-kfOei7nf.js → vendor-arizeai-CSF-1Kc5.js} +1 -1
  52. phoenix/server/static/assets/{vendor-codemirror-1bq_t1Ec.js → vendor-codemirror-Bv8J_7an.js} +3 -3
  53. phoenix/server/static/assets/{vendor-recharts-DQ4xfrf4.js → vendor-recharts-DcLgzI7g.js} +1 -1
  54. phoenix/server/static/assets/{vendor-shiki-GGmcIQxA.js → vendor-shiki-BF8rh_7m.js} +1 -1
  55. phoenix/version.py +1 -1
  56. {arize_phoenix-12.4.0.dist-info → arize_phoenix-12.5.0.dist-info}/WHEEL +0 -0
  57. {arize_phoenix-12.4.0.dist-info → arize_phoenix-12.5.0.dist-info}/entry_points.txt +0 -0
  58. {arize_phoenix-12.4.0.dist-info → arize_phoenix-12.5.0.dist-info}/licenses/IP_NOTICE +0 -0
  59. {arize_phoenix-12.4.0.dist-info → arize_phoenix-12.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -23,7 +23,7 @@ from phoenix.db.types.annotation_configs import (
23
23
  from phoenix.db.types.annotation_configs import (
24
24
  FreeformAnnotationConfig as FreeformAnnotationConfigModel,
25
25
  )
26
- from phoenix.server.api.auth import IsNotReadOnly
26
+ from phoenix.server.api.auth import IsNotReadOnly, IsNotViewer
27
27
  from phoenix.server.api.context import Context
28
28
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
29
29
  from phoenix.server.api.queries import Query
@@ -197,7 +197,7 @@ def _to_pydantic_freeform_annotation_config(
197
197
 
198
198
  @strawberry.type
199
199
  class AnnotationConfigMutationMixin:
200
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore[misc]
200
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore[misc]
201
201
  async def create_annotation_config(
202
202
  self,
203
203
  info: Info[Context, None],
@@ -236,7 +236,7 @@ class AnnotationConfigMutationMixin:
236
236
  annotation_config=to_gql_annotation_config(annotation_config),
237
237
  )
238
238
 
239
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore[misc]
239
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore[misc]
240
240
  async def update_annotation_config(
241
241
  self,
242
242
  info: Info[Context, None],
@@ -285,7 +285,7 @@ class AnnotationConfigMutationMixin:
285
285
  annotation_config=to_gql_annotation_config(annotation_config),
286
286
  )
287
287
 
288
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore[misc]
288
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore[misc]
289
289
  async def delete_annotation_configs(
290
290
  self,
291
291
  info: Info[Context, None],
@@ -317,7 +317,7 @@ class AnnotationConfigMutationMixin:
317
317
  ],
318
318
  )
319
319
 
320
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore[misc]
320
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore[misc]
321
321
  async def add_annotation_config_to_project(
322
322
  self,
323
323
  info: Info[Context, None],
@@ -377,7 +377,7 @@ class AnnotationConfigMutationMixin:
377
377
  project=Project(project_rowid=project_id),
378
378
  )
379
379
 
380
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore[misc]
380
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore[misc]
381
381
  async def remove_annotation_config_from_project(
382
382
  self,
383
383
  info: Info[Context, None],
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime, timezone
2
- from typing import Optional
2
+ from typing import Literal, Optional
3
3
 
4
4
  import strawberry
5
5
  from sqlalchemy import select
@@ -9,7 +9,7 @@ from strawberry.types import Info
9
9
 
10
10
  from phoenix.db import models
11
11
  from phoenix.db.models import UserRoleName
12
- from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly
12
+ from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly, IsNotViewer
13
13
  from phoenix.server.api.context import Context
14
14
  from phoenix.server.api.exceptions import Unauthorized
15
15
  from phoenix.server.api.queries import Query
@@ -61,7 +61,7 @@ class DeleteApiKeyMutationPayload:
61
61
 
62
62
  @strawberry.type
63
63
  class ApiKeyMutationMixin:
64
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin, IsLocked]) # type: ignore
64
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsAdmin, IsLocked]) # type: ignore
65
65
  async def create_system_api_key(
66
66
  self, info: Info[Context, None], input: CreateApiKeyInput
67
67
  ) -> CreateSystemApiKeyMutationPayload:
@@ -113,12 +113,20 @@ class ApiKeyMutationMixin:
113
113
  except AttributeError:
114
114
  raise ValueError("User not found")
115
115
  issued_at = datetime.now(timezone.utc)
116
+ # Determine user role for API key
117
+ user_role: Literal["ADMIN", "MEMBER", "VIEWER"]
118
+ if user.is_admin:
119
+ user_role = "ADMIN"
120
+ elif user.is_viewer:
121
+ user_role = "VIEWER"
122
+ else:
123
+ user_role = "MEMBER"
116
124
  claims = ApiKeyClaims(
117
125
  subject=user.identity,
118
126
  issued_at=issued_at,
119
127
  expiration_time=input.expires_at or None,
120
128
  attributes=ApiKeyAttributes(
121
- user_role="ADMIN" if user.is_admin else "MEMBER",
129
+ user_role=user_role,
122
130
  name=input.name,
123
131
  description=input.description,
124
132
  ),
@@ -137,7 +145,7 @@ class ApiKeyMutationMixin:
137
145
  query=Query(),
138
146
  )
139
147
 
140
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin]) # type: ignore
148
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsAdmin]) # type: ignore
141
149
  async def delete_system_api_key(
142
150
  self, info: Info[Context, None], input: DeleteApiKeyInput
143
151
  ) -> DeleteApiKeyMutationPayload:
@@ -30,7 +30,7 @@ from phoenix.db.helpers import (
30
30
  get_dataset_example_revisions,
31
31
  insert_experiment_with_examples_snapshot,
32
32
  )
33
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
33
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
34
34
  from phoenix.server.api.context import Context
35
35
  from phoenix.server.api.exceptions import BadRequest, CustomGraphQLError, NotFound
36
36
  from phoenix.server.api.helpers.dataset_helpers import get_dataset_example_output
@@ -131,7 +131,7 @@ class ChatCompletionOverDatasetMutationPayload:
131
131
 
132
132
  @strawberry.type
133
133
  class ChatCompletionMutationMixin:
134
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
134
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
135
135
  @classmethod
136
136
  async def chat_completion_over_dataset(
137
137
  cls,
@@ -302,7 +302,7 @@ class ChatCompletionMutationMixin:
302
302
  payload.examples.append(example_payload)
303
303
  return payload
304
304
 
305
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
305
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
306
306
  @classmethod
307
307
  async def chat_completion(
308
308
  cls, info: Info[Context, None], input: ChatCompletionInput
@@ -10,7 +10,7 @@ from strawberry.relay.types import GlobalID
10
10
  from strawberry.types import Info
11
11
 
12
12
  from phoenix.db import models
13
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
13
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
14
14
  from phoenix.server.api.context import Context
15
15
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
16
16
  from phoenix.server.api.queries import Query
@@ -78,7 +78,7 @@ class UnsetDatasetLabelsMutationPayload:
78
78
 
79
79
  @strawberry.type
80
80
  class DatasetLabelMutationMixin:
81
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
81
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
82
82
  async def create_dataset_label(
83
83
  self,
84
84
  info: Info[Context, None],
@@ -100,7 +100,7 @@ class DatasetLabelMutationMixin:
100
100
  dataset_label=to_gql_dataset_label(dataset_label_orm)
101
101
  )
102
102
 
103
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
103
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
104
104
  async def update_dataset_label(
105
105
  self, info: Info[Context, None], input: UpdateDatasetLabelInput
106
106
  ) -> UpdateDatasetLabelMutationPayload:
@@ -133,7 +133,7 @@ class DatasetLabelMutationMixin:
133
133
  dataset_label=to_gql_dataset_label(dataset_label_orm)
134
134
  )
135
135
 
136
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
136
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
137
137
  async def delete_dataset_labels(
138
138
  self, info: Info[Context, None], input: DeleteDatasetLabelsInput
139
139
  ) -> DeleteDatasetLabelsMutationPayload:
@@ -166,7 +166,7 @@ class DatasetLabelMutationMixin:
166
166
  ]
167
167
  )
168
168
 
169
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
169
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
170
170
  async def set_dataset_labels(
171
171
  self, info: Info[Context, None], input: SetDatasetLabelsInput
172
172
  ) -> SetDatasetLabelsMutationPayload:
@@ -248,7 +248,7 @@ class DatasetLabelMutationMixin:
248
248
  query=Query(),
249
249
  )
250
250
 
251
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
251
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
252
252
  async def unset_dataset_labels(
253
253
  self, info: Info[Context, None], input: UnsetDatasetLabelsInput
254
254
  ) -> UnsetDatasetLabelsMutationPayload:
@@ -18,7 +18,7 @@ from strawberry.types import Info
18
18
 
19
19
  from phoenix.db import models
20
20
  from phoenix.db.helpers import get_eval_trace_ids_for_datasets, get_project_names_for_datasets
21
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
21
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
22
22
  from phoenix.server.api.context import Context
23
23
  from phoenix.server.api.exceptions import BadRequest, NotFound
24
24
  from phoenix.server.api.helpers.dataset_helpers import (
@@ -50,7 +50,7 @@ class DatasetMutationPayload:
50
50
 
51
51
  @strawberry.type
52
52
  class DatasetMutationMixin:
53
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
53
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
54
54
  async def create_dataset(
55
55
  self,
56
56
  info: Info[Context, None],
@@ -74,7 +74,7 @@ class DatasetMutationMixin:
74
74
  info.context.event_queue.put(DatasetInsertEvent((dataset.id,)))
75
75
  return DatasetMutationPayload(dataset=to_gql_dataset(dataset))
76
76
 
77
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
77
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
78
78
  async def patch_dataset(
79
79
  self,
80
80
  info: Info[Context, None],
@@ -103,7 +103,7 @@ class DatasetMutationMixin:
103
103
  info.context.event_queue.put(DatasetInsertEvent((dataset.id,)))
104
104
  return DatasetMutationPayload(dataset=to_gql_dataset(dataset))
105
105
 
106
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
106
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
107
107
  async def add_spans_to_dataset(
108
108
  self,
109
109
  info: Info[Context, None],
@@ -223,7 +223,7 @@ class DatasetMutationMixin:
223
223
  info.context.event_queue.put(DatasetInsertEvent((dataset.id,)))
224
224
  return DatasetMutationPayload(dataset=to_gql_dataset(dataset))
225
225
 
226
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
226
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
227
227
  async def add_examples_to_dataset(
228
228
  self, info: Info[Context, None], input: AddExamplesToDatasetInput
229
229
  ) -> DatasetMutationPayload:
@@ -350,7 +350,7 @@ class DatasetMutationMixin:
350
350
  info.context.event_queue.put(DatasetInsertEvent((dataset.id,)))
351
351
  return DatasetMutationPayload(dataset=to_gql_dataset(dataset))
352
352
 
353
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
353
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
354
354
  async def delete_dataset(
355
355
  self,
356
356
  info: Info[Context, None],
@@ -381,7 +381,7 @@ class DatasetMutationMixin:
381
381
  info.context.event_queue.put(DatasetDeleteEvent((dataset.id,)))
382
382
  return DatasetMutationPayload(dataset=to_gql_dataset(dataset))
383
383
 
384
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
384
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
385
385
  async def patch_dataset_examples(
386
386
  self,
387
387
  info: Info[Context, None],
@@ -474,7 +474,7 @@ class DatasetMutationMixin:
474
474
  info.context.event_queue.put(DatasetInsertEvent((dataset.id,)))
475
475
  return DatasetMutationPayload(dataset=to_gql_dataset(dataset))
476
476
 
477
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
477
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
478
478
  async def delete_dataset_examples(
479
479
  self, info: Info[Context, None], input: DeleteDatasetExamplesInput
480
480
  ) -> DatasetMutationPayload:
@@ -10,7 +10,7 @@ from strawberry.scalars import JSON
10
10
  from strawberry.types import Info
11
11
 
12
12
  from phoenix.db import models
13
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
13
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
14
14
  from phoenix.server.api.context import Context
15
15
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
16
16
  from phoenix.server.api.helpers.playground_users import get_user
@@ -96,7 +96,7 @@ class RemoveDatasetExamplesFromDatasetSplitsMutationPayload:
96
96
 
97
97
  @strawberry.type
98
98
  class DatasetSplitMutationMixin:
99
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
99
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
100
100
  async def create_dataset_split(
101
101
  self, info: Info[Context, None], input: CreateDatasetSplitInput
102
102
  ) -> DatasetSplitMutationPayload:
@@ -119,7 +119,7 @@ class DatasetSplitMutationMixin:
119
119
  dataset_split=to_gql_dataset_split(dataset_split_orm), query=Query()
120
120
  )
121
121
 
122
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
122
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
123
123
  async def patch_dataset_split(
124
124
  self, info: Info[Context, None], input: PatchDatasetSplitInput
125
125
  ) -> DatasetSplitMutationPayload:
@@ -152,7 +152,7 @@ class DatasetSplitMutationMixin:
152
152
  query=Query(),
153
153
  )
154
154
 
155
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
155
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
156
156
  async def delete_dataset_splits(
157
157
  self, info: Info[Context, None], input: DeleteDatasetSplitInput
158
158
  ) -> DeleteDatasetSplitsMutationPayload:
@@ -191,7 +191,7 @@ class DatasetSplitMutationMixin:
191
191
  query=Query(),
192
192
  )
193
193
 
194
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
194
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
195
195
  async def add_dataset_examples_to_dataset_splits(
196
196
  self, info: Info[Context, None], input: AddDatasetExamplesToDatasetSplitsInput
197
197
  ) -> AddDatasetExamplesToDatasetSplitsMutationPayload:
@@ -284,7 +284,7 @@ class DatasetSplitMutationMixin:
284
284
  examples=[to_gql_dataset_example(example) for example in examples],
285
285
  )
286
286
 
287
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
287
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
288
288
  async def remove_dataset_examples_from_dataset_splits(
289
289
  self, info: Info[Context, None], input: RemoveDatasetExamplesFromDatasetSplitsInput
290
290
  ) -> RemoveDatasetExamplesFromDatasetSplitsMutationPayload:
@@ -345,7 +345,7 @@ class DatasetSplitMutationMixin:
345
345
  examples=[to_gql_dataset_example(example) for example in examples],
346
346
  )
347
347
 
348
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
348
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
349
349
  async def create_dataset_split_with_examples(
350
350
  self, info: Info[Context, None], input: CreateDatasetSplitWithExamplesInput
351
351
  ) -> DatasetSplitMutationPayloadWithExamples:
@@ -7,7 +7,7 @@ from strawberry.types import Info
7
7
 
8
8
  from phoenix.db import models
9
9
  from phoenix.db.helpers import get_eval_trace_ids_for_experiments, get_project_names_for_experiments
10
- from phoenix.server.api.auth import IsNotReadOnly
10
+ from phoenix.server.api.auth import IsNotReadOnly, IsNotViewer
11
11
  from phoenix.server.api.context import Context
12
12
  from phoenix.server.api.exceptions import CustomGraphQLError
13
13
  from phoenix.server.api.input_types.DeleteExperimentsInput import DeleteExperimentsInput
@@ -24,7 +24,7 @@ class ExperimentMutationPayload:
24
24
 
25
25
  @strawberry.type
26
26
  class ExperimentMutationMixin:
27
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
27
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
28
28
  async def delete_experiments(
29
29
  self,
30
30
  info: Info[Context, None],
@@ -8,7 +8,7 @@ from strawberry import ID, UNSET
8
8
  from strawberry.types import Info
9
9
 
10
10
  import phoenix.core.model_schema as ms
11
- from phoenix.server.api.auth import IsNotReadOnly
11
+ from phoenix.server.api.auth import IsNotReadOnly, IsNotViewer
12
12
  from phoenix.server.api.context import Context
13
13
  from phoenix.server.api.input_types.ClusterInput import ClusterInput
14
14
  from phoenix.server.api.types.Event import parse_event_ids_by_inferences_role, unpack_event_id
@@ -19,7 +19,7 @@ from phoenix.server.api.types.InferencesRole import AncillaryInferencesRole, Inf
19
19
  @strawberry.type
20
20
  class ExportEventsMutationMixin:
21
21
  @strawberry.mutation(
22
- permission_classes=[IsNotReadOnly],
22
+ permission_classes=[IsNotReadOnly, IsNotViewer],
23
23
  description=(
24
24
  "Given a list of event ids, export the corresponding data subset in Parquet format."
25
25
  " File name is optional, but if specified, should be without file extension. By default"
@@ -51,7 +51,7 @@ class ExportEventsMutationMixin:
51
51
  return ExportedFile(file_name=file_name)
52
52
 
53
53
  @strawberry.mutation(
54
- permission_classes=[IsNotReadOnly],
54
+ permission_classes=[IsNotReadOnly, IsNotViewer],
55
55
  description=(
56
56
  "Given a list of clusters, export the corresponding data subset in Parquet format."
57
57
  " File name is optional, but if specified, should be without file extension. By default"
@@ -12,7 +12,7 @@ from strawberry.relay import GlobalID
12
12
  from strawberry.types import Info
13
13
 
14
14
  from phoenix.db import models
15
- from phoenix.server.api.auth import IsNotReadOnly
15
+ from phoenix.server.api.auth import IsNotReadOnly, IsNotViewer
16
16
  from phoenix.server.api.context import Context
17
17
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
18
18
  from phoenix.server.api.queries import Query
@@ -81,7 +81,7 @@ class DeleteModelMutationPayload:
81
81
 
82
82
  @strawberry.type
83
83
  class ModelMutationMixin:
84
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
84
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
85
85
  async def create_model(
86
86
  self,
87
87
  info: Info[Context, None],
@@ -114,7 +114,7 @@ class ModelMutationMixin:
114
114
  query=Query(),
115
115
  )
116
116
 
117
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
117
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
118
118
  async def update_model(
119
119
  self,
120
120
  info: Info[Context, None],
@@ -167,7 +167,7 @@ class ModelMutationMixin:
167
167
  query=Query(),
168
168
  )
169
169
 
170
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
170
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
171
171
  async def delete_model(
172
172
  self,
173
173
  info: Info[Context, None],
@@ -8,7 +8,7 @@ from strawberry.types import Info
8
8
 
9
9
  from phoenix.config import DEFAULT_PROJECT_NAME
10
10
  from phoenix.db import models
11
- from phoenix.server.api.auth import IsNotReadOnly
11
+ from phoenix.server.api.auth import IsNotReadOnly, IsNotViewer
12
12
  from phoenix.server.api.context import Context
13
13
  from phoenix.server.api.exceptions import BadRequest, Conflict
14
14
  from phoenix.server.api.input_types.ClearProjectInput import ClearProjectInput
@@ -27,7 +27,7 @@ class ProjectMutationPayload:
27
27
 
28
28
  @strawberry.type
29
29
  class ProjectMutationMixin:
30
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
30
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
31
31
  async def create_project(
32
32
  self,
33
33
  info: Info[Context, None],
@@ -52,7 +52,7 @@ class ProjectMutationMixin:
52
52
  info.context.event_queue.put(ProjectInsertEvent((project.id,)))
53
53
  return ProjectMutationPayload(project=to_gql_project(project), query=Query())
54
54
 
55
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
55
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
56
56
  async def delete_project(self, info: Info[Context, None], id: GlobalID) -> Query:
57
57
  project_id = from_global_id_with_expected_type(global_id=id, expected_type_name="Project")
58
58
  async with info.context.db() as session:
@@ -69,7 +69,7 @@ class ProjectMutationMixin:
69
69
  info.context.event_queue.put(ProjectDeleteEvent((project_id,)))
70
70
  return Query()
71
71
 
72
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
72
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
73
73
  async def clear_project(self, info: Info[Context, None], input: ClearProjectInput) -> Query:
74
74
  project_id = from_global_id_with_expected_type(
75
75
  global_id=input.id, expected_type_name="Project"
@@ -8,7 +8,7 @@ from strawberry import Info
8
8
  from strawberry.relay import GlobalID
9
9
 
10
10
  from phoenix.db import models
11
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
11
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
12
12
  from phoenix.server.api.context import Context
13
13
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound, Unauthorized
14
14
  from phoenix.server.api.helpers.annotations import get_user_identifier
@@ -38,7 +38,7 @@ class ProjectSessionAnnotationMutationPayload:
38
38
 
39
39
  @strawberry.type
40
40
  class ProjectSessionAnnotationMutationMixin:
41
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
41
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
42
42
  async def create_project_session_annotations(
43
43
  self, info: Info[Context, None], input: CreateProjectSessionAnnotationInput
44
44
  ) -> ProjectSessionAnnotationMutationPayload:
@@ -85,7 +85,7 @@ class ProjectSessionAnnotationMutationMixin:
85
85
  query=Query(),
86
86
  )
87
87
 
88
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
88
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
89
89
  async def update_project_session_annotations(
90
90
  self, info: Info[Context, None], input: UpdateAnnotationInput
91
91
  ) -> ProjectSessionAnnotationMutationPayload:
@@ -126,7 +126,7 @@ class ProjectSessionAnnotationMutationMixin:
126
126
  query=Query(),
127
127
  )
128
128
 
129
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
129
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
130
130
  async def delete_project_session_annotation(
131
131
  self, info: Info[Context, None], id: GlobalID
132
132
  ) -> ProjectSessionAnnotationMutationPayload:
@@ -16,7 +16,7 @@ from phoenix.db.types.trace_retention import (
16
16
  TraceRetentionCronExpression,
17
17
  TraceRetentionRule,
18
18
  )
19
- from phoenix.server.api.auth import IsAdminIfAuthEnabled, IsLocked, IsNotReadOnly
19
+ from phoenix.server.api.auth import IsAdminIfAuthEnabled, IsLocked, IsNotReadOnly, IsNotViewer
20
20
  from phoenix.server.api.context import Context
21
21
  from phoenix.server.api.exceptions import BadRequest, NotFound
22
22
  from phoenix.server.api.queries import Query
@@ -113,7 +113,9 @@ class ProjectTraceRetentionPolicyMutationPayload:
113
113
 
114
114
  @strawberry.type
115
115
  class ProjectTraceRetentionPolicyMutationMixin:
116
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdminIfAuthEnabled, IsLocked]) # type: ignore
116
+ @strawberry.mutation(
117
+ permission_classes=[IsNotReadOnly, IsNotViewer, IsAdminIfAuthEnabled, IsLocked]
118
+ ) # type: ignore
117
119
  async def create_project_trace_retention_policy(
118
120
  self,
119
121
  info: Info[Context, None],
@@ -146,7 +148,9 @@ class ProjectTraceRetentionPolicyMutationMixin:
146
148
  node=ProjectTraceRetentionPolicy(id=policy.id, db_policy=policy),
147
149
  )
148
150
 
149
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdminIfAuthEnabled, IsLocked]) # type: ignore
151
+ @strawberry.mutation(
152
+ permission_classes=[IsNotReadOnly, IsNotViewer, IsAdminIfAuthEnabled, IsLocked]
153
+ ) # type: ignore
150
154
  async def patch_project_trace_retention_policy(
151
155
  self,
152
156
  info: Info[Context, None],
@@ -204,7 +208,7 @@ class ProjectTraceRetentionPolicyMutationMixin:
204
208
  node=ProjectTraceRetentionPolicy(id=policy.id, db_policy=policy),
205
209
  )
206
210
 
207
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdminIfAuthEnabled]) # type: ignore
211
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsAdminIfAuthEnabled]) # type: ignore
208
212
  async def delete_project_trace_retention_policy(
209
213
  self,
210
214
  info: Info[Context, None],
@@ -10,7 +10,7 @@ from strawberry.relay import GlobalID
10
10
  from strawberry.types import Info
11
11
 
12
12
  from phoenix.db import models
13
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
13
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
14
14
  from phoenix.server.api.context import Context
15
15
  from phoenix.server.api.exceptions import Conflict, NotFound
16
16
  from phoenix.server.api.queries import Query
@@ -69,7 +69,7 @@ class PromptLabelAssociationMutationPayload:
69
69
 
70
70
  @strawberry.type
71
71
  class PromptLabelMutationMixin:
72
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
72
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
73
73
  async def create_prompt_label(
74
74
  self, info: Info[Context, None], input: CreatePromptLabelInput
75
75
  ) -> PromptLabelMutationPayload:
@@ -89,7 +89,7 @@ class PromptLabelMutationMixin:
89
89
  query=Query(),
90
90
  )
91
91
 
92
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
92
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
93
93
  async def patch_prompt_label(
94
94
  self, info: Info[Context, None], input: PatchPromptLabelInput
95
95
  ) -> PromptLabelMutationPayload:
@@ -117,7 +117,7 @@ class PromptLabelMutationMixin:
117
117
  query=Query(),
118
118
  )
119
119
 
120
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
120
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
121
121
  async def delete_prompt_labels(
122
122
  self, info: Info[Context, None], input: DeletePromptLabelsInput
123
123
  ) -> PromptLabelDeleteMutationPayload:
@@ -139,7 +139,7 @@ class PromptLabelMutationMixin:
139
139
  query=Query(),
140
140
  )
141
141
 
142
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
142
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
143
143
  async def set_prompt_labels(
144
144
  self, info: Info[Context, None], input: SetPromptLabelsInput
145
145
  ) -> PromptLabelAssociationMutationPayload:
@@ -168,7 +168,7 @@ class PromptLabelMutationMixin:
168
168
  query=Query(),
169
169
  )
170
170
 
171
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
171
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
172
172
  async def unset_prompt_labels(
173
173
  self, info: Info[Context, None], input: UnsetPromptLabelsInput
174
174
  ) -> PromptLabelAssociationMutationPayload:
@@ -188,7 +188,7 @@ class PromptLabelMutationMixin:
188
188
  )
189
189
  result = await session.execute(stmt)
190
190
 
191
- if result.rowcount != len(label_ids):
191
+ if result.rowcount != len(label_ids): # type: ignore[attr-defined]
192
192
  label_ids_str = ", ".join(str(i) for i in label_ids)
193
193
  raise NotFound(
194
194
  f"No association between prompt={prompt_id} and labels={label_ids_str}."
@@ -13,7 +13,7 @@ from strawberry.types import Info
13
13
  from phoenix.db import models
14
14
  from phoenix.db.types.identifier import Identifier as IdentifierModel
15
15
  from phoenix.db.types.model_provider import ModelProvider
16
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
16
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
17
17
  from phoenix.server.api.context import Context
18
18
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
19
19
  from phoenix.server.api.helpers.prompts.models import (
@@ -75,7 +75,7 @@ class DeletePromptMutationPayload:
75
75
 
76
76
  @strawberry.type
77
77
  class PromptMutationMixin:
78
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
78
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
79
79
  async def create_chat_prompt(
80
80
  self, info: Info[Context, None], input: CreateChatPromptInput
81
81
  ) -> Prompt:
@@ -142,7 +142,7 @@ class PromptMutationMixin:
142
142
  raise Conflict(f"A prompt named '{input.name}' already exists")
143
143
  return to_gql_prompt_from_orm(prompt)
144
144
 
145
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
145
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
146
146
  async def create_chat_prompt_version(
147
147
  self,
148
148
  info: Info[Context, None],
@@ -220,7 +220,7 @@ class PromptMutationMixin:
220
220
 
221
221
  return to_gql_prompt_from_orm(prompt)
222
222
 
223
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
223
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
224
224
  async def delete_prompt(
225
225
  self, info: Info[Context, None], input: DeletePromptInput
226
226
  ) -> DeletePromptMutationPayload:
@@ -231,13 +231,13 @@ class PromptMutationMixin:
231
231
  stmt = delete(models.Prompt).where(models.Prompt.id == prompt_id)
232
232
  result = await session.execute(stmt)
233
233
 
234
- if result.rowcount == 0:
234
+ if result.rowcount == 0: # type: ignore[attr-defined]
235
235
  raise NotFound(f"Prompt with ID '{input.prompt_id}' not found")
236
236
 
237
237
  await session.commit()
238
238
  return DeletePromptMutationPayload(query=Query())
239
239
 
240
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
240
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
241
241
  async def clone_prompt(self, info: Info[Context, None], input: ClonePromptInput) -> Prompt:
242
242
  prompt_id = from_global_id_with_expected_type(
243
243
  global_id=input.prompt_id, expected_type_name=Prompt.__name__
@@ -290,7 +290,7 @@ class PromptMutationMixin:
290
290
  raise Conflict(f"A prompt named '{input.name}' already exists")
291
291
  return to_gql_prompt_from_orm(new_prompt)
292
292
 
293
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
293
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
294
294
  async def patch_prompt(self, info: Info[Context, None], input: PatchPromptInput) -> Prompt:
295
295
  prompt_id = from_global_id_with_expected_type(
296
296
  global_id=input.prompt_id, expected_type_name=Prompt.__name__
@@ -10,7 +10,7 @@ from strawberry.types import Info
10
10
 
11
11
  from phoenix.db import models
12
12
  from phoenix.db.types.identifier import Identifier as IdentifierModel
13
- from phoenix.server.api.auth import IsLocked, IsNotReadOnly
13
+ from phoenix.server.api.auth import IsLocked, IsNotReadOnly, IsNotViewer
14
14
  from phoenix.server.api.context import Context
15
15
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound
16
16
  from phoenix.server.api.queries import Query
@@ -42,7 +42,7 @@ class PromptVersionTagMutationPayload:
42
42
 
43
43
  @strawberry.type
44
44
  class PromptVersionTagMutationMixin:
45
- @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
45
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer]) # type: ignore
46
46
  async def delete_prompt_version_tag(
47
47
  self, info: Info[Context, None], input: DeletePromptVersionTagInput
48
48
  ) -> PromptVersionTagMutationPayload:
@@ -78,7 +78,7 @@ class PromptVersionTagMutationMixin:
78
78
  prompt_version_tag=None, query=Query(), prompt=to_gql_prompt_from_orm(prompt)
79
79
  )
80
80
 
81
- @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
81
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsNotViewer, IsLocked]) # type: ignore
82
82
  async def set_prompt_version_tag(
83
83
  self, info: Info[Context, None], input: SetPromptVersionTagInput
84
84
  ) -> PromptVersionTagMutationPayload: