arize-phoenix 11.23.1__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.
Files changed (221) hide show
  1. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +61 -36
  2. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/RECORD +212 -162
  3. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
  4. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/IP_NOTICE +1 -1
  5. phoenix/__generated__/__init__.py +0 -0
  6. phoenix/__generated__/classification_evaluator_configs/__init__.py +20 -0
  7. phoenix/__generated__/classification_evaluator_configs/_document_relevance_classification_evaluator_config.py +17 -0
  8. phoenix/__generated__/classification_evaluator_configs/_hallucination_classification_evaluator_config.py +17 -0
  9. phoenix/__generated__/classification_evaluator_configs/_models.py +18 -0
  10. phoenix/__generated__/classification_evaluator_configs/_tool_selection_classification_evaluator_config.py +17 -0
  11. phoenix/__init__.py +2 -1
  12. phoenix/auth.py +27 -2
  13. phoenix/config.py +1594 -81
  14. phoenix/db/README.md +546 -28
  15. phoenix/db/bulk_inserter.py +119 -116
  16. phoenix/db/engines.py +140 -33
  17. phoenix/db/facilitator.py +22 -1
  18. phoenix/db/helpers.py +818 -65
  19. phoenix/db/iam_auth.py +64 -0
  20. phoenix/db/insertion/dataset.py +133 -1
  21. phoenix/db/insertion/document_annotation.py +9 -6
  22. phoenix/db/insertion/evaluation.py +2 -3
  23. phoenix/db/insertion/helpers.py +2 -2
  24. phoenix/db/insertion/session_annotation.py +176 -0
  25. phoenix/db/insertion/span_annotation.py +3 -4
  26. phoenix/db/insertion/trace_annotation.py +3 -4
  27. phoenix/db/insertion/types.py +41 -18
  28. phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
  29. phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
  30. phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
  31. phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
  32. phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
  33. phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
  34. phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
  35. phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
  36. phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
  37. phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
  38. phoenix/db/models.py +364 -56
  39. phoenix/db/pg_config.py +10 -0
  40. phoenix/db/types/trace_retention.py +7 -6
  41. phoenix/experiments/functions.py +69 -19
  42. phoenix/inferences/inferences.py +1 -2
  43. phoenix/server/api/auth.py +9 -0
  44. phoenix/server/api/auth_messages.py +46 -0
  45. phoenix/server/api/context.py +60 -0
  46. phoenix/server/api/dataloaders/__init__.py +36 -0
  47. phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
  48. phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
  49. phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
  50. phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
  51. phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
  52. phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
  53. phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
  54. phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
  55. phoenix/server/api/dataloaders/dataset_labels.py +36 -0
  56. phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
  57. phoenix/server/api/dataloaders/document_evaluations.py +6 -9
  58. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
  59. phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
  60. phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
  61. phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
  62. phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
  63. phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
  64. phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
  65. phoenix/server/api/dataloaders/record_counts.py +37 -10
  66. phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
  67. phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
  68. phoenix/server/api/dataloaders/span_cost_summary_by_project.py +28 -14
  69. phoenix/server/api/dataloaders/span_costs.py +3 -9
  70. phoenix/server/api/dataloaders/table_fields.py +2 -2
  71. phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
  72. phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
  73. phoenix/server/api/exceptions.py +5 -1
  74. phoenix/server/api/helpers/playground_clients.py +263 -83
  75. phoenix/server/api/helpers/playground_spans.py +2 -1
  76. phoenix/server/api/helpers/playground_users.py +26 -0
  77. phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
  78. phoenix/server/api/helpers/prompts/models.py +61 -19
  79. phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
  80. phoenix/server/api/input_types/ChatCompletionInput.py +3 -0
  81. phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
  82. phoenix/server/api/input_types/DatasetFilter.py +5 -2
  83. phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
  84. phoenix/server/api/input_types/GenerativeModelInput.py +3 -0
  85. phoenix/server/api/input_types/ProjectSessionSort.py +158 -1
  86. phoenix/server/api/input_types/PromptVersionInput.py +47 -1
  87. phoenix/server/api/input_types/SpanSort.py +3 -2
  88. phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
  89. phoenix/server/api/input_types/UserRoleInput.py +1 -0
  90. phoenix/server/api/mutations/__init__.py +8 -0
  91. phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
  92. phoenix/server/api/mutations/api_key_mutations.py +15 -20
  93. phoenix/server/api/mutations/chat_mutations.py +106 -37
  94. phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
  95. phoenix/server/api/mutations/dataset_mutations.py +21 -16
  96. phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
  97. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  98. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  99. phoenix/server/api/mutations/model_mutations.py +11 -9
  100. phoenix/server/api/mutations/project_mutations.py +4 -4
  101. phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
  102. phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
  103. phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
  104. phoenix/server/api/mutations/prompt_mutations.py +65 -129
  105. phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
  106. phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
  107. phoenix/server/api/mutations/trace_annotations_mutations.py +13 -8
  108. phoenix/server/api/mutations/trace_mutations.py +3 -3
  109. phoenix/server/api/mutations/user_mutations.py +55 -26
  110. phoenix/server/api/queries.py +501 -617
  111. phoenix/server/api/routers/__init__.py +2 -2
  112. phoenix/server/api/routers/auth.py +141 -87
  113. phoenix/server/api/routers/ldap.py +229 -0
  114. phoenix/server/api/routers/oauth2.py +349 -101
  115. phoenix/server/api/routers/v1/__init__.py +22 -4
  116. phoenix/server/api/routers/v1/annotation_configs.py +19 -30
  117. phoenix/server/api/routers/v1/annotations.py +455 -13
  118. phoenix/server/api/routers/v1/datasets.py +355 -68
  119. phoenix/server/api/routers/v1/documents.py +142 -0
  120. phoenix/server/api/routers/v1/evaluations.py +20 -28
  121. phoenix/server/api/routers/v1/experiment_evaluations.py +16 -6
  122. phoenix/server/api/routers/v1/experiment_runs.py +335 -59
  123. phoenix/server/api/routers/v1/experiments.py +475 -47
  124. phoenix/server/api/routers/v1/projects.py +16 -50
  125. phoenix/server/api/routers/v1/prompts.py +50 -39
  126. phoenix/server/api/routers/v1/sessions.py +108 -0
  127. phoenix/server/api/routers/v1/spans.py +156 -96
  128. phoenix/server/api/routers/v1/traces.py +51 -77
  129. phoenix/server/api/routers/v1/users.py +64 -24
  130. phoenix/server/api/routers/v1/utils.py +3 -7
  131. phoenix/server/api/subscriptions.py +257 -93
  132. phoenix/server/api/types/Annotation.py +90 -23
  133. phoenix/server/api/types/ApiKey.py +13 -17
  134. phoenix/server/api/types/AuthMethod.py +1 -0
  135. phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
  136. phoenix/server/api/types/Dataset.py +199 -72
  137. phoenix/server/api/types/DatasetExample.py +88 -18
  138. phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
  139. phoenix/server/api/types/DatasetLabel.py +57 -0
  140. phoenix/server/api/types/DatasetSplit.py +98 -0
  141. phoenix/server/api/types/DatasetVersion.py +49 -4
  142. phoenix/server/api/types/DocumentAnnotation.py +212 -0
  143. phoenix/server/api/types/Experiment.py +215 -68
  144. phoenix/server/api/types/ExperimentComparison.py +3 -9
  145. phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
  146. phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
  147. phoenix/server/api/types/ExperimentRun.py +120 -70
  148. phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
  149. phoenix/server/api/types/GenerativeModel.py +95 -42
  150. phoenix/server/api/types/GenerativeProvider.py +1 -1
  151. phoenix/server/api/types/ModelInterface.py +7 -2
  152. phoenix/server/api/types/PlaygroundModel.py +12 -2
  153. phoenix/server/api/types/Project.py +218 -185
  154. phoenix/server/api/types/ProjectSession.py +146 -29
  155. phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
  156. phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
  157. phoenix/server/api/types/Prompt.py +119 -39
  158. phoenix/server/api/types/PromptLabel.py +42 -25
  159. phoenix/server/api/types/PromptVersion.py +11 -8
  160. phoenix/server/api/types/PromptVersionTag.py +65 -25
  161. phoenix/server/api/types/Span.py +130 -123
  162. phoenix/server/api/types/SpanAnnotation.py +189 -42
  163. phoenix/server/api/types/SystemApiKey.py +65 -1
  164. phoenix/server/api/types/Trace.py +184 -53
  165. phoenix/server/api/types/TraceAnnotation.py +149 -50
  166. phoenix/server/api/types/User.py +128 -33
  167. phoenix/server/api/types/UserApiKey.py +73 -26
  168. phoenix/server/api/types/node.py +10 -0
  169. phoenix/server/api/types/pagination.py +11 -2
  170. phoenix/server/app.py +154 -36
  171. phoenix/server/authorization.py +5 -4
  172. phoenix/server/bearer_auth.py +13 -5
  173. phoenix/server/cost_tracking/cost_model_lookup.py +42 -14
  174. phoenix/server/cost_tracking/model_cost_manifest.json +1085 -194
  175. phoenix/server/daemons/generative_model_store.py +61 -9
  176. phoenix/server/daemons/span_cost_calculator.py +10 -8
  177. phoenix/server/dml_event.py +13 -0
  178. phoenix/server/email/sender.py +29 -2
  179. phoenix/server/grpc_server.py +9 -9
  180. phoenix/server/jwt_store.py +8 -6
  181. phoenix/server/ldap.py +1449 -0
  182. phoenix/server/main.py +9 -3
  183. phoenix/server/oauth2.py +330 -12
  184. phoenix/server/prometheus.py +43 -6
  185. phoenix/server/rate_limiters.py +4 -9
  186. phoenix/server/retention.py +33 -20
  187. phoenix/server/session_filters.py +49 -0
  188. phoenix/server/static/.vite/manifest.json +51 -53
  189. phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
  190. phoenix/server/static/assets/{index-BPCwGQr8.js → index-CTQoemZv.js} +42 -35
  191. phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
  192. phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
  193. phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
  194. phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
  195. phoenix/server/static/assets/{vendor-recharts-Bw30oz1A.js → vendor-recharts-V9cwpXsm.js} +7 -7
  196. phoenix/server/static/assets/{vendor-shiki-DZajAPeq.js → vendor-shiki-Do--csgv.js} +1 -1
  197. phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
  198. phoenix/server/templates/index.html +7 -1
  199. phoenix/server/thread_server.py +1 -2
  200. phoenix/server/utils.py +74 -0
  201. phoenix/session/client.py +55 -1
  202. phoenix/session/data_extractor.py +5 -0
  203. phoenix/session/evaluation.py +8 -4
  204. phoenix/session/session.py +44 -8
  205. phoenix/settings.py +2 -0
  206. phoenix/trace/attributes.py +80 -13
  207. phoenix/trace/dsl/query.py +2 -0
  208. phoenix/trace/projects.py +5 -0
  209. phoenix/utilities/template_formatters.py +1 -1
  210. phoenix/version.py +1 -1
  211. phoenix/server/api/types/Evaluation.py +0 -39
  212. phoenix/server/static/assets/components-D0DWAf0l.js +0 -5650
  213. phoenix/server/static/assets/pages-Creyamao.js +0 -8612
  214. phoenix/server/static/assets/vendor-CU36oj8y.js +0 -905
  215. phoenix/server/static/assets/vendor-CqDb5u4o.css +0 -1
  216. phoenix/server/static/assets/vendor-arizeai-Ctgw0e1G.js +0 -168
  217. phoenix/server/static/assets/vendor-codemirror-Cojjzqb9.js +0 -25
  218. phoenix/server/static/assets/vendor-three-BLWp5bic.js +0 -2998
  219. phoenix/utilities/deprecation.py +0 -31
  220. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
  221. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,8 @@
1
- from typing import Optional
1
+ from datetime import datetime
2
+ from math import isfinite
3
+ from typing import TYPE_CHECKING, Annotated, Optional
2
4
 
3
5
  import strawberry
4
- from strawberry import Private
5
6
  from strawberry.relay import GlobalID, Node, NodeID
6
7
  from strawberry.scalars import JSON
7
8
  from strawberry.types import Info
@@ -12,56 +13,202 @@ from phoenix.server.api.context import Context
12
13
  from .Annotation import Annotation
13
14
  from .AnnotationSource import AnnotationSource
14
15
  from .AnnotatorKind import AnnotatorKind
15
- from .User import User, to_gql_user
16
+
17
+ if TYPE_CHECKING:
18
+ from .Span import Span
19
+ from .User import User
16
20
 
17
21
 
18
22
  @strawberry.type
19
23
  class SpanAnnotation(Node, Annotation):
20
- id_attr: NodeID[int]
21
- user_id: Private[Optional[int]]
22
- annotator_kind: AnnotatorKind
23
- metadata: JSON
24
- span_rowid: Private[Optional[int]]
25
- source: AnnotationSource
26
- identifier: str
24
+ id: NodeID[int]
25
+ db_record: strawberry.Private[Optional[models.SpanAnnotation]] = None
26
+
27
+ def __post_init__(self) -> None:
28
+ if self.db_record and self.id != self.db_record.id:
29
+ raise ValueError("SpanAnnotation ID mismatch")
30
+
31
+ @strawberry.field(description="Name of the annotation, e.g. 'helpfulness' or 'relevance'.") # type: ignore
32
+ async def name(
33
+ self,
34
+ info: Info[Context, None],
35
+ ) -> str:
36
+ if self.db_record:
37
+ val = self.db_record.name
38
+ else:
39
+ val = await info.context.data_loaders.span_annotation_fields.load(
40
+ (self.id, models.SpanAnnotation.name),
41
+ )
42
+ return val
43
+
44
+ @strawberry.field(description="The kind of annotator that produced the annotation.") # type: ignore
45
+ async def annotator_kind(
46
+ self,
47
+ info: Info[Context, None],
48
+ ) -> AnnotatorKind:
49
+ if self.db_record:
50
+ val = self.db_record.annotator_kind
51
+ else:
52
+ val = await info.context.data_loaders.span_annotation_fields.load(
53
+ (self.id, models.SpanAnnotation.annotator_kind),
54
+ )
55
+ return AnnotatorKind(val)
56
+
57
+ @strawberry.field(
58
+ description="Value of the annotation in the form of a string, e.g. "
59
+ "'helpful' or 'not helpful'. Note that the label is not necessarily binary."
60
+ ) # type: ignore
61
+ async def label(
62
+ self,
63
+ info: Info[Context, None],
64
+ ) -> Optional[str]:
65
+ if self.db_record:
66
+ val = self.db_record.label
67
+ else:
68
+ val = await info.context.data_loaders.span_annotation_fields.load(
69
+ (self.id, models.SpanAnnotation.label),
70
+ )
71
+ return val
72
+
73
+ @strawberry.field(
74
+ description="Value of the annotation in the form of a numeric score.",
75
+ ) # type: ignore
76
+ async def score(
77
+ self,
78
+ info: Info[Context, None],
79
+ ) -> Optional[float]:
80
+ if self.db_record:
81
+ val = self.db_record.score
82
+ else:
83
+ val = await info.context.data_loaders.span_annotation_fields.load(
84
+ (self.id, models.SpanAnnotation.score),
85
+ )
86
+ return val if val is not None and isfinite(val) else None
87
+
88
+ @strawberry.field(
89
+ description="The annotator's explanation for the annotation result (i.e. "
90
+ "score or label, or both) given to the subject."
91
+ ) # type: ignore
92
+ async def explanation(
93
+ self,
94
+ info: Info[Context, None],
95
+ ) -> Optional[str]:
96
+ if self.db_record:
97
+ val = self.db_record.explanation
98
+ else:
99
+ val = await info.context.data_loaders.span_annotation_fields.load(
100
+ (self.id, models.SpanAnnotation.explanation),
101
+ )
102
+ return val
103
+
104
+ @strawberry.field(description="Metadata about the annotation.") # type: ignore
105
+ async def metadata(
106
+ self,
107
+ info: Info[Context, None],
108
+ ) -> JSON:
109
+ if self.db_record:
110
+ val = self.db_record.metadata_
111
+ else:
112
+ val = await info.context.data_loaders.span_annotation_fields.load(
113
+ (self.id, models.SpanAnnotation.metadata_),
114
+ )
115
+ return val
116
+
117
+ @strawberry.field(description="The source of the annotation.") # type: ignore
118
+ async def source(
119
+ self,
120
+ info: Info[Context, None],
121
+ ) -> AnnotationSource:
122
+ if self.db_record:
123
+ val = self.db_record.source
124
+ else:
125
+ val = await info.context.data_loaders.span_annotation_fields.load(
126
+ (self.id, models.SpanAnnotation.source),
127
+ )
128
+ return AnnotationSource(val)
129
+
130
+ @strawberry.field(description="The identifier of the annotation.") # type: ignore
131
+ async def identifier(
132
+ self,
133
+ info: Info[Context, None],
134
+ ) -> str:
135
+ if self.db_record:
136
+ val = self.db_record.identifier
137
+ else:
138
+ val = await info.context.data_loaders.span_annotation_fields.load(
139
+ (self.id, models.SpanAnnotation.identifier),
140
+ )
141
+ return val
142
+
143
+ @strawberry.field(description="The date and time when the annotation was created.") # type: ignore
144
+ async def created_at(
145
+ self,
146
+ info: Info[Context, None],
147
+ ) -> datetime:
148
+ if self.db_record:
149
+ val = self.db_record.created_at
150
+ else:
151
+ val = await info.context.data_loaders.span_annotation_fields.load(
152
+ (self.id, models.SpanAnnotation.created_at),
153
+ )
154
+ return val
155
+
156
+ @strawberry.field(description="The date and time when the annotation was last updated.") # type: ignore
157
+ async def updated_at(
158
+ self,
159
+ info: Info[Context, None],
160
+ ) -> datetime:
161
+ if self.db_record:
162
+ val = self.db_record.updated_at
163
+ else:
164
+ val = await info.context.data_loaders.span_annotation_fields.load(
165
+ (self.id, models.SpanAnnotation.updated_at),
166
+ )
167
+ return val
27
168
 
28
169
  @strawberry.field
29
- async def span_id(self) -> GlobalID:
170
+ async def span_id(
171
+ self,
172
+ info: Info[Context, None],
173
+ ) -> GlobalID:
30
174
  from phoenix.server.api.types.Span import Span
31
175
 
32
- return GlobalID(type_name=Span.__name__, node_id=str(self.span_rowid))
176
+ if self.db_record:
177
+ span_rowid = self.db_record.span_rowid
178
+ else:
179
+ span_rowid = await info.context.data_loaders.span_annotation_fields.load(
180
+ (self.id, models.SpanAnnotation.span_rowid),
181
+ )
182
+ return GlobalID(type_name=Span.__name__, node_id=str(span_rowid))
33
183
 
34
- @strawberry.field
184
+ @strawberry.field(description="The span associated with the annotation.") # type: ignore
185
+ async def span(
186
+ self,
187
+ info: Info[Context, None],
188
+ ) -> Annotated["Span", strawberry.lazy(".Span")]:
189
+ if self.db_record:
190
+ span_rowid = self.db_record.span_rowid
191
+ else:
192
+ span_rowid = await info.context.data_loaders.span_annotation_fields.load(
193
+ (self.id, models.SpanAnnotation.span_rowid),
194
+ )
195
+ from .Span import Span
196
+
197
+ return Span(id=span_rowid)
198
+
199
+ @strawberry.field(description="The user that produced the annotation.") # type: ignore
35
200
  async def user(
36
201
  self,
37
202
  info: Info[Context, None],
38
- ) -> Optional[User]:
39
- if self.user_id is None:
203
+ ) -> Optional[Annotated["User", strawberry.lazy(".User")]]:
204
+ if self.db_record:
205
+ user_id = self.db_record.user_id
206
+ else:
207
+ user_id = await info.context.data_loaders.span_annotation_fields.load(
208
+ (self.id, models.SpanAnnotation.user_id),
209
+ )
210
+ if user_id is None:
40
211
  return None
41
- user = await info.context.data_loaders.users.load(self.user_id)
42
- if user is None:
43
- return None
44
- return to_gql_user(user)
45
-
46
-
47
- def to_gql_span_annotation(
48
- annotation: models.SpanAnnotation,
49
- ) -> SpanAnnotation:
50
- """
51
- Converts an ORM span annotation to a GraphQL SpanAnnotation.
52
- """
53
- return SpanAnnotation(
54
- id_attr=annotation.id,
55
- user_id=annotation.user_id,
56
- span_rowid=annotation.span_rowid,
57
- name=annotation.name,
58
- annotator_kind=AnnotatorKind(annotation.annotator_kind),
59
- label=annotation.label,
60
- score=annotation.score,
61
- explanation=annotation.explanation,
62
- metadata=annotation.metadata_,
63
- source=AnnotationSource(annotation.source),
64
- identifier=annotation.identifier,
65
- created_at=annotation.created_at,
66
- updated_at=annotation.updated_at,
67
- )
212
+ from .User import User
213
+
214
+ return User(id=user_id)
@@ -1,9 +1,73 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+
1
4
  import strawberry
2
5
  from strawberry.relay import Node, NodeID
6
+ from strawberry.types import Info
7
+
8
+ from phoenix.db.models import ApiKey as OrmApiKey
9
+ from phoenix.server.api.context import Context
3
10
 
4
11
  from .ApiKey import ApiKey
5
12
 
6
13
 
7
14
  @strawberry.type
8
15
  class SystemApiKey(ApiKey, Node):
9
- id_attr: NodeID[int]
16
+ id: NodeID[int]
17
+ db_record: strawberry.Private[Optional[OrmApiKey]] = None
18
+
19
+ def __post_init__(self) -> None:
20
+ if self.db_record and self.id != self.db_record.id:
21
+ raise ValueError("SystemApiKey ID mismatch")
22
+
23
+ @strawberry.field(description="Name of the API key.") # type: ignore
24
+ async def name(
25
+ self,
26
+ info: Info[Context, None],
27
+ ) -> str:
28
+ if self.db_record:
29
+ val = self.db_record.name
30
+ else:
31
+ val = await info.context.data_loaders.user_api_key_fields.load(
32
+ (self.id, OrmApiKey.name),
33
+ )
34
+ return val
35
+
36
+ @strawberry.field(description="Description of the API key.") # type: ignore
37
+ async def description(
38
+ self,
39
+ info: Info[Context, None],
40
+ ) -> Optional[str]:
41
+ if self.db_record:
42
+ val = self.db_record.description
43
+ else:
44
+ val = await info.context.data_loaders.user_api_key_fields.load(
45
+ (self.id, OrmApiKey.description),
46
+ )
47
+ return val
48
+
49
+ @strawberry.field(description="The date and time the API key was created.") # type: ignore
50
+ async def created_at(
51
+ self,
52
+ info: Info[Context, None],
53
+ ) -> datetime:
54
+ if self.db_record:
55
+ val = self.db_record.created_at
56
+ else:
57
+ val = await info.context.data_loaders.user_api_key_fields.load(
58
+ (self.id, OrmApiKey.created_at),
59
+ )
60
+ return val
61
+
62
+ @strawberry.field(description="The date and time the API key will expire.") # type: ignore
63
+ async def expires_at(
64
+ self,
65
+ info: Info[Context, None],
66
+ ) -> Optional[datetime]:
67
+ if self.db_record:
68
+ val = self.db_record.expires_at
69
+ else:
70
+ val = await info.context.data_loaders.user_api_key_fields.load(
71
+ (self.id, OrmApiKey.expires_at),
72
+ )
73
+ return val