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
@@ -222,6 +222,7 @@ def get_db_experiment_run(
222
222
  *,
223
223
  experiment_id: int,
224
224
  example_id: int,
225
+ repetition_number: int,
225
226
  ) -> models.ExperimentRun:
226
227
  return models.ExperimentRun(
227
228
  experiment_id=experiment_id,
@@ -230,7 +231,7 @@ def get_db_experiment_run(
230
231
  output=models.ExperimentRunOutput(
231
232
  task_output=get_dataset_example_output(db_span),
232
233
  ),
233
- repetition_number=1,
234
+ repetition_number=repetition_number,
234
235
  start_time=db_span.start_time,
235
236
  end_time=db_span.end_time,
236
237
  error=db_span.status_message or None,
@@ -0,0 +1,26 @@
1
+ from typing import (
2
+ Optional,
3
+ )
4
+
5
+ from starlette.requests import Request
6
+ from strawberry import Info
7
+
8
+ from phoenix.server.api.context import Context
9
+ from phoenix.server.bearer_auth import PhoenixUser
10
+
11
+
12
+ def get_user(info: Info[Context, None]) -> Optional[int]:
13
+ user_id: Optional[int] = None
14
+ try:
15
+ assert isinstance(request := info.context.request, Request)
16
+
17
+ if "user" in request.scope and isinstance((user := info.context.user), PhoenixUser):
18
+ user_id = int(user.identity)
19
+ except AssertionError:
20
+ # Request is not available, try to obtain user identify
21
+ # this will also throw an assertion error if auth is not available
22
+ # the finally block will continue execution returning None
23
+ if info.context.user.is_authenticated:
24
+ user_id = int(info.context.user.identity)
25
+ finally:
26
+ return user_id
@@ -0,0 +1,103 @@
1
+ from typing import TYPE_CHECKING, Any, Literal, Union
2
+
3
+ from typing_extensions import NotRequired, TypedDict, assert_never
4
+
5
+ if TYPE_CHECKING:
6
+ from phoenix.server.api.helpers.prompts.models import (
7
+ PromptToolChoiceNone,
8
+ PromptToolChoiceOneOrMore,
9
+ PromptToolChoiceSpecificFunctionTool,
10
+ PromptToolChoiceZeroOrMore,
11
+ )
12
+
13
+
14
+ class GoogleFunctionCallingConfig(TypedDict, total=False):
15
+ """
16
+ Based on https://github.com/googleapis/python-genai/blob/97cc7e4eafbee4fa4035e7420170ab6a2c9da7fb/google/genai/types.py#L4245
17
+ """
18
+
19
+ mode: NotRequired[Literal["auto", "any", "none"]]
20
+ allowed_function_names: NotRequired[list[str]]
21
+
22
+
23
+ class GoogleToolChoice(TypedDict):
24
+ """
25
+ Based on https://github.com/googleapis/python-genai/blob/97cc7e4eafbee4fa4035e7420170ab6a2c9da7fb/google/genai/types.py#L4341
26
+ """
27
+
28
+ function_calling_config: GoogleFunctionCallingConfig
29
+
30
+
31
+ class GoogleToolChoiceConversion:
32
+ @staticmethod
33
+ def to_google(
34
+ obj: Union[
35
+ "PromptToolChoiceNone",
36
+ "PromptToolChoiceZeroOrMore",
37
+ "PromptToolChoiceOneOrMore",
38
+ "PromptToolChoiceSpecificFunctionTool",
39
+ ],
40
+ ) -> GoogleToolChoice:
41
+ if obj.type == "none":
42
+ return {"function_calling_config": {"mode": "none"}}
43
+ if obj.type == "zero_or_more":
44
+ return {"function_calling_config": {"mode": "auto"}}
45
+ if obj.type == "one_or_more":
46
+ return {"function_calling_config": {"mode": "any"}}
47
+ if obj.type == "specific_function":
48
+ return {
49
+ "function_calling_config": {
50
+ "mode": "any",
51
+ "allowed_function_names": [obj.function_name],
52
+ }
53
+ }
54
+ assert_never(obj)
55
+
56
+ @staticmethod
57
+ def from_google(
58
+ obj: Any,
59
+ ) -> Union[
60
+ "PromptToolChoiceNone",
61
+ "PromptToolChoiceZeroOrMore",
62
+ "PromptToolChoiceOneOrMore",
63
+ "PromptToolChoiceSpecificFunctionTool",
64
+ ]:
65
+ from google.genai.types import ToolConfig
66
+
67
+ from phoenix.server.api.helpers.prompts.models import (
68
+ PromptToolChoiceNone,
69
+ PromptToolChoiceOneOrMore,
70
+ PromptToolChoiceSpecificFunctionTool,
71
+ PromptToolChoiceZeroOrMore,
72
+ )
73
+
74
+ tool_config = ToolConfig.model_validate(obj)
75
+ if (function_calling_config := tool_config.function_calling_config) is None:
76
+ raise ValueError("function_calling_config is required")
77
+ # normalize mode to lowercase since Google's API is case-insensitive
78
+ # https://github.com/googleapis/python-genai/blob/97cc7e4eafbee4fa4035e7420170ab6a2c9da7fb/google/genai/types.py#L645
79
+ normalized_mode = (
80
+ function_calling_config.mode.value.lower()
81
+ if function_calling_config.mode is not None
82
+ else None
83
+ )
84
+ allowed_function_names = function_calling_config.allowed_function_names
85
+
86
+ if allowed_function_names:
87
+ if len(allowed_function_names) != 1:
88
+ raise ValueError("Only one allowed function name is currently supported")
89
+ if normalized_mode != "any":
90
+ raise ValueError("allowed function names only supported in 'any' mode")
91
+ return PromptToolChoiceSpecificFunctionTool(
92
+ type="specific_function",
93
+ function_name=allowed_function_names[0],
94
+ )
95
+
96
+ if normalized_mode == "none":
97
+ return PromptToolChoiceNone(type="none")
98
+ if normalized_mode == "auto" or normalized_mode is None:
99
+ return PromptToolChoiceZeroOrMore(type="zero_or_more")
100
+ if normalized_mode == "any":
101
+ return PromptToolChoiceOneOrMore(type="one_or_more")
102
+
103
+ raise ValueError(f"Unsupported Google tool choice mode: {normalized_mode}")
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  from enum import Enum
4
2
  from typing import Any, Literal, Mapping, Optional, Union
5
3
 
@@ -10,6 +8,7 @@ from phoenix.db.types.db_models import UNDEFINED, DBBaseModel
10
8
  from phoenix.db.types.model_provider import ModelProvider
11
9
  from phoenix.server.api.helpers.prompts.conversions.anthropic import AnthropicToolChoiceConversion
12
10
  from phoenix.server.api.helpers.prompts.conversions.aws import AwsToolChoiceConversion
11
+ from phoenix.server.api.helpers.prompts.conversions.google import GoogleToolChoiceConversion
13
12
  from phoenix.server.api.helpers.prompts.conversions.openai import OpenAIToolChoiceConversion
14
13
 
15
14
  JSONSerializable = Union[None, bool, int, float, str, dict[str, Any], list[Any]]
@@ -127,11 +126,6 @@ class PromptTemplateRootModel(RootModel[PromptTemplate]):
127
126
  root: PromptTemplate
128
127
 
129
128
 
130
- class PromptToolFunction(DBBaseModel):
131
- type: Literal["function"]
132
- function: PromptToolFunctionDefinition
133
-
134
-
135
129
  class PromptToolFunctionDefinition(DBBaseModel):
136
130
  name: str
137
131
  description: str = UNDEFINED
@@ -139,14 +133,12 @@ class PromptToolFunctionDefinition(DBBaseModel):
139
133
  strict: bool = UNDEFINED
140
134
 
141
135
 
142
- PromptTool: TypeAlias = Annotated[Union[PromptToolFunction], Field(..., discriminator="type")]
136
+ class PromptToolFunction(DBBaseModel):
137
+ type: Literal["function"]
138
+ function: PromptToolFunctionDefinition
143
139
 
144
140
 
145
- class PromptTools(DBBaseModel):
146
- type: Literal["tools"]
147
- tools: Annotated[list[PromptTool], Field(..., min_length=1)]
148
- tool_choice: PromptToolChoice = UNDEFINED
149
- disable_parallel_tool_calls: bool = UNDEFINED
141
+ PromptTool: TypeAlias = Annotated[Union[PromptToolFunction], Field(..., discriminator="type")]
150
142
 
151
143
 
152
144
  class PromptToolChoiceNone(DBBaseModel):
@@ -177,6 +169,13 @@ PromptToolChoice: TypeAlias = Annotated[
177
169
  ]
178
170
 
179
171
 
172
+ class PromptTools(DBBaseModel):
173
+ type: Literal["tools"]
174
+ tools: Annotated[list[PromptTool], Field(..., min_length=1)]
175
+ tool_choice: PromptToolChoice = UNDEFINED
176
+ disable_parallel_tool_calls: bool = UNDEFINED
177
+
178
+
180
179
  class PromptOpenAIJSONSchema(DBBaseModel):
181
180
  """
182
181
  Based on https://github.com/openai/openai-python/blob/d16e6edde5a155626910b5758a0b939bfedb9ced/src/openai/types/shared/response_format_json_schema.py#L13
@@ -200,11 +199,6 @@ class PromptOpenAIResponseFormatJSONSchema(DBBaseModel):
200
199
  type: Literal["json_schema"]
201
200
 
202
201
 
203
- class PromptResponseFormatJSONSchema(DBBaseModel):
204
- type: Literal["json_schema"]
205
- json_schema: PromptResponseFormatJSONSchemaDefinition
206
-
207
-
208
202
  class PromptResponseFormatJSONSchemaDefinition(DBBaseModel):
209
203
  name: str
210
204
  description: str = UNDEFINED
@@ -212,6 +206,11 @@ class PromptResponseFormatJSONSchemaDefinition(DBBaseModel):
212
206
  strict: bool = UNDEFINED
213
207
 
214
208
 
209
+ class PromptResponseFormatJSONSchema(DBBaseModel):
210
+ type: Literal["json_schema"]
211
+ json_schema: PromptResponseFormatJSONSchemaDefinition
212
+
213
+
215
214
  PromptResponseFormat: TypeAlias = Annotated[
216
215
  Union[PromptResponseFormatJSONSchema], Field(..., discriminator="type")
217
216
  ]
@@ -321,6 +320,16 @@ class BedrockToolDefinition(DBBaseModel):
321
320
  toolSpec: dict[str, Any]
322
321
 
323
322
 
323
+ class GeminiToolDefinition(DBBaseModel):
324
+ """
325
+ Based on https://github.com/googleapis/python-genai/blob/c0b175a0ca20286db419390031a2239938d0c0b7/google/genai/types.py#L2792
326
+ """
327
+
328
+ name: str
329
+ description: str = UNDEFINED
330
+ parameters: dict[str, Any]
331
+
332
+
324
333
  class PromptOpenAIInvocationParametersContent(DBBaseModel):
325
334
  temperature: float = UNDEFINED
326
335
  max_tokens: int = UNDEFINED
@@ -329,7 +338,7 @@ class PromptOpenAIInvocationParametersContent(DBBaseModel):
329
338
  presence_penalty: float = UNDEFINED
330
339
  top_p: float = UNDEFINED
331
340
  seed: int = UNDEFINED
332
- reasoning_effort: Literal["minimal", "low", "medium", "high"] = UNDEFINED
341
+ reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] = UNDEFINED
333
342
 
334
343
 
335
344
  class PromptOpenAIInvocationParameters(DBBaseModel):
@@ -565,6 +574,9 @@ def normalize_tools(
565
574
  elif model_provider is ModelProvider.ANTHROPIC:
566
575
  anthropic_tools = [AnthropicToolDefinition.model_validate(schema) for schema in schemas]
567
576
  tools = [_anthropic_to_prompt_tool(anthropic_tool) for anthropic_tool in anthropic_tools]
577
+ elif model_provider is ModelProvider.GOOGLE:
578
+ gemini_tools = [GeminiToolDefinition.model_validate(schema) for schema in schemas]
579
+ tools = [_gemini_to_prompt_tool(gemini_tool) for gemini_tool in gemini_tools]
568
580
  else:
569
581
  raise ValueError(f"Unsupported model provider: {model_provider}")
570
582
  ans = PromptTools(type="tools", tools=tools)
@@ -587,6 +599,8 @@ def normalize_tools(
587
599
  ans.tool_choice = choice
588
600
  if disable_parallel_tool_calls is not None:
589
601
  ans.disable_parallel_tool_calls = disable_parallel_tool_calls
602
+ elif model_provider is ModelProvider.GOOGLE:
603
+ ans.tool_choice = GoogleToolChoiceConversion.from_google(tool_choice)
590
604
  return ans
591
605
 
592
606
 
@@ -614,6 +628,10 @@ def denormalize_tools(
614
628
  denormalized_tools = [_prompt_to_anthropic_tool(tool) for tool in tools.tools]
615
629
  if tools.tool_choice and tools.tool_choice.type != "none":
616
630
  tool_choice = AnthropicToolChoiceConversion.to_anthropic(tools.tool_choice)
631
+ elif model_provider is ModelProvider.GOOGLE:
632
+ denormalized_tools = [_prompt_to_gemini_tool(tool) for tool in tools.tools]
633
+ if tools.tool_choice:
634
+ tool_choice = GoogleToolChoiceConversion.to_google(tools.tool_choice)
617
635
  else:
618
636
  raise ValueError(f"Unsupported model provider: {model_provider}")
619
637
  return [tool.model_dump() for tool in denormalized_tools], tool_choice
@@ -703,3 +721,27 @@ def _prompt_to_bedrock_tool(
703
721
  },
704
722
  }
705
723
  )
724
+
725
+
726
+ def _gemini_to_prompt_tool(
727
+ tool: GeminiToolDefinition,
728
+ ) -> PromptToolFunction:
729
+ return PromptToolFunction(
730
+ type="function",
731
+ function=PromptToolFunctionDefinition(
732
+ name=tool.name,
733
+ description=tool.description,
734
+ parameters=tool.parameters,
735
+ ),
736
+ )
737
+
738
+
739
+ def _prompt_to_gemini_tool(
740
+ tool: PromptToolFunction,
741
+ ) -> GeminiToolDefinition:
742
+ function = tool.function
743
+ return GeminiToolDefinition(
744
+ name=function.name,
745
+ description=function.description,
746
+ parameters=function.parameters,
747
+ )
@@ -1,8 +1,9 @@
1
- from typing import Optional
1
+ from typing import Optional, Union
2
2
 
3
3
  import strawberry
4
4
  from strawberry import UNSET
5
5
  from strawberry.relay import GlobalID
6
+ from typing_extensions import TypeAlias
6
7
 
7
8
  from phoenix.db import models
8
9
  from phoenix.server.api.exceptions import BadRequest
@@ -11,7 +12,7 @@ from phoenix.server.api.types.node import from_global_id_with_expected_type
11
12
 
12
13
 
13
14
  @strawberry.input
14
- class SpanAnnotationFilterCondition:
15
+ class AnnotationFilterCondition:
15
16
  names: Optional[list[str]] = UNSET
16
17
  sources: Optional[list[AnnotationSource]] = UNSET
17
18
  user_ids: Optional[list[Optional[GlobalID]]] = UNSET
@@ -26,42 +27,49 @@ class SpanAnnotationFilterCondition:
26
27
 
27
28
 
28
29
  @strawberry.input
29
- class SpanAnnotationFilter:
30
- include: Optional[SpanAnnotationFilterCondition] = UNSET
31
- exclude: Optional[SpanAnnotationFilterCondition] = UNSET
30
+ class AnnotationFilter:
31
+ include: Optional[AnnotationFilterCondition] = UNSET
32
+ exclude: Optional[AnnotationFilterCondition] = UNSET
32
33
 
33
34
  def __post_init__(self) -> None:
34
35
  if self.include is UNSET and self.exclude is UNSET:
35
36
  raise BadRequest("include and exclude cannot both be unset")
36
37
 
37
38
 
38
- def satisfies_filter(span_annotation: models.SpanAnnotation, filter: SpanAnnotationFilter) -> bool:
39
+ _Annotation: TypeAlias = Union[
40
+ models.SpanAnnotation,
41
+ models.TraceAnnotation,
42
+ models.ProjectSessionAnnotation,
43
+ ]
44
+
45
+
46
+ def satisfies_filter(annotation: _Annotation, filter: AnnotationFilter) -> bool:
39
47
  """
40
- Returns true if the span annotation satisfies the filter and false otherwise.
48
+ Returns true if the annotation satisfies the filter and false otherwise.
41
49
  """
42
- span_annotation_source = AnnotationSource(span_annotation.source)
50
+ annotation_source = AnnotationSource(annotation.source)
43
51
  if include := filter.include:
44
- if include.names and span_annotation.name not in include.names:
52
+ if include.names and annotation.name not in include.names:
45
53
  return False
46
- if include.sources and span_annotation_source not in include.sources:
54
+ if include.sources and annotation_source not in include.sources:
47
55
  return False
48
56
  if include.user_ids:
49
57
  user_rowids = [
50
58
  from_global_id_with_expected_type(user_id, "User") if user_id is not None else None
51
59
  for user_id in include.user_ids
52
60
  ]
53
- if span_annotation.user_id not in user_rowids:
61
+ if annotation.user_id not in user_rowids:
54
62
  return False
55
63
  if exclude := filter.exclude:
56
- if exclude.names and span_annotation.name in exclude.names:
64
+ if exclude.names and annotation.name in exclude.names:
57
65
  return False
58
- if exclude.sources and span_annotation_source in exclude.sources:
66
+ if exclude.sources and annotation_source in exclude.sources:
59
67
  return False
60
68
  if exclude.user_ids:
61
69
  user_rowids = [
62
70
  from_global_id_with_expected_type(user_id, "User") if user_id is not None else None
63
71
  for user_id in exclude.user_ids
64
72
  ]
65
- if span_annotation.user_id in user_rowids:
73
+ if annotation.user_id in user_rowids:
66
74
  return False
67
75
  return True
@@ -26,6 +26,7 @@ class ChatCompletionInput:
26
26
  credentials: Optional[list[GenerativeCredentialInput]] = UNSET
27
27
  template: Optional[PromptTemplateOptions] = UNSET
28
28
  prompt_name: Optional[Identifier] = None
29
+ repetitions: int
29
30
 
30
31
 
31
32
  @strawberry.input
@@ -36,8 +37,10 @@ class ChatCompletionOverDatasetInput:
36
37
  tools: Optional[list[JSON]] = UNSET
37
38
  credentials: Optional[list[GenerativeCredentialInput]] = UNSET
38
39
  template_format: PromptTemplateFormat = PromptTemplateFormat.MUSTACHE
40
+ repetitions: int
39
41
  dataset_id: GlobalID
40
42
  dataset_version_id: Optional[GlobalID] = None
43
+ split_ids: Optional[list[GlobalID]] = None
41
44
  experiment_name: Optional[str] = None
42
45
  experiment_description: Optional[str] = None
43
46
  experiment_metadata: Optional[JSON] = strawberry.field(default_factory=dict)
@@ -0,0 +1,37 @@
1
+ from typing import Optional
2
+
3
+ import strawberry
4
+ from strawberry.relay import GlobalID
5
+ from strawberry.scalars import JSON
6
+
7
+ from phoenix.server.api.exceptions import BadRequest
8
+ from phoenix.server.api.types.AnnotationSource import AnnotationSource
9
+ from phoenix.server.api.types.AnnotatorKind import AnnotatorKind
10
+
11
+
12
+ @strawberry.input
13
+ class CreateProjectSessionAnnotationInput:
14
+ project_session_id: GlobalID
15
+ name: str
16
+ annotator_kind: AnnotatorKind = AnnotatorKind.HUMAN
17
+ label: Optional[str] = None
18
+ score: Optional[float] = None
19
+ explanation: Optional[str] = None
20
+ metadata: JSON = strawberry.field(default_factory=dict)
21
+ source: AnnotationSource = AnnotationSource.APP
22
+ identifier: Optional[str] = strawberry.UNSET
23
+
24
+ def __post_init__(self) -> None:
25
+ self.name = self.name.strip()
26
+ if isinstance(self.label, str):
27
+ self.label = self.label.strip()
28
+ if not self.label:
29
+ self.label = None
30
+ if isinstance(self.explanation, str):
31
+ self.explanation = self.explanation.strip()
32
+ if not self.explanation:
33
+ self.explanation = None
34
+ if isinstance(self.identifier, str):
35
+ self.identifier = self.identifier.strip()
36
+ if self.score is None and not self.label and not self.explanation:
37
+ raise BadRequest("At least one of score, label, or explanation must be not null/empty.")
@@ -1,6 +1,8 @@
1
1
  from enum import Enum
2
+ from typing import Optional
2
3
 
3
4
  import strawberry
5
+ from strawberry import UNSET
4
6
 
5
7
 
6
8
  @strawberry.enum
@@ -10,5 +12,6 @@ class DatasetFilterColumn(Enum):
10
12
 
11
13
  @strawberry.input(description="A filter for datasets")
12
14
  class DatasetFilter:
13
- col: DatasetFilterColumn
14
- value: str
15
+ col: Optional[DatasetFilterColumn] = None
16
+ value: Optional[str] = None
17
+ filter_labels: Optional[list[str]] = UNSET