arize-phoenix 4.14.1__py3-none-any.whl → 4.16.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 (85) hide show
  1. {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/METADATA +5 -3
  2. {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/RECORD +81 -71
  3. phoenix/db/bulk_inserter.py +131 -5
  4. phoenix/db/engines.py +2 -1
  5. phoenix/db/helpers.py +23 -1
  6. phoenix/db/insertion/constants.py +2 -0
  7. phoenix/db/insertion/document_annotation.py +157 -0
  8. phoenix/db/insertion/helpers.py +13 -0
  9. phoenix/db/insertion/span_annotation.py +144 -0
  10. phoenix/db/insertion/trace_annotation.py +144 -0
  11. phoenix/db/insertion/types.py +261 -0
  12. phoenix/experiments/functions.py +3 -2
  13. phoenix/experiments/types.py +3 -3
  14. phoenix/server/api/context.py +7 -9
  15. phoenix/server/api/dataloaders/__init__.py +2 -0
  16. phoenix/server/api/dataloaders/average_experiment_run_latency.py +3 -3
  17. phoenix/server/api/dataloaders/dataset_example_revisions.py +2 -4
  18. phoenix/server/api/dataloaders/dataset_example_spans.py +2 -4
  19. phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -4
  20. phoenix/server/api/dataloaders/document_evaluations.py +2 -4
  21. phoenix/server/api/dataloaders/document_retrieval_metrics.py +2 -4
  22. phoenix/server/api/dataloaders/evaluation_summaries.py +2 -4
  23. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +2 -4
  24. phoenix/server/api/dataloaders/experiment_error_rates.py +2 -4
  25. phoenix/server/api/dataloaders/experiment_run_counts.py +2 -4
  26. phoenix/server/api/dataloaders/experiment_sequence_number.py +2 -4
  27. phoenix/server/api/dataloaders/latency_ms_quantile.py +2 -3
  28. phoenix/server/api/dataloaders/min_start_or_max_end_times.py +2 -4
  29. phoenix/server/api/dataloaders/project_by_name.py +3 -3
  30. phoenix/server/api/dataloaders/record_counts.py +2 -4
  31. phoenix/server/api/dataloaders/span_annotations.py +2 -4
  32. phoenix/server/api/dataloaders/span_dataset_examples.py +36 -0
  33. phoenix/server/api/dataloaders/span_descendants.py +2 -4
  34. phoenix/server/api/dataloaders/span_evaluations.py +2 -4
  35. phoenix/server/api/dataloaders/span_projects.py +3 -3
  36. phoenix/server/api/dataloaders/token_counts.py +2 -4
  37. phoenix/server/api/dataloaders/trace_evaluations.py +2 -4
  38. phoenix/server/api/dataloaders/trace_row_ids.py +2 -4
  39. phoenix/server/api/input_types/SpanAnnotationSort.py +17 -0
  40. phoenix/server/api/input_types/TraceAnnotationSort.py +17 -0
  41. phoenix/server/api/mutations/span_annotations_mutations.py +8 -3
  42. phoenix/server/api/mutations/trace_annotations_mutations.py +8 -3
  43. phoenix/server/api/openapi/main.py +18 -2
  44. phoenix/server/api/openapi/schema.py +12 -12
  45. phoenix/server/api/routers/v1/__init__.py +36 -83
  46. phoenix/server/api/routers/v1/datasets.py +515 -509
  47. phoenix/server/api/routers/v1/evaluations.py +164 -73
  48. phoenix/server/api/routers/v1/experiment_evaluations.py +68 -91
  49. phoenix/server/api/routers/v1/experiment_runs.py +98 -155
  50. phoenix/server/api/routers/v1/experiments.py +132 -181
  51. phoenix/server/api/routers/v1/pydantic_compat.py +78 -0
  52. phoenix/server/api/routers/v1/spans.py +164 -203
  53. phoenix/server/api/routers/v1/traces.py +134 -159
  54. phoenix/server/api/routers/v1/utils.py +95 -0
  55. phoenix/server/api/types/Span.py +27 -3
  56. phoenix/server/api/types/Trace.py +21 -4
  57. phoenix/server/api/utils.py +4 -4
  58. phoenix/server/app.py +172 -192
  59. phoenix/server/grpc_server.py +2 -2
  60. phoenix/server/main.py +5 -9
  61. phoenix/server/static/.vite/manifest.json +31 -31
  62. phoenix/server/static/assets/components-Ci5kMOk5.js +1175 -0
  63. phoenix/server/static/assets/{index-CQgXRwU0.js → index-BQG5WVX7.js} +2 -2
  64. phoenix/server/static/assets/{pages-hdjlFZhO.js → pages-BrevprVW.js} +451 -275
  65. phoenix/server/static/assets/{vendor-DPvSDRn3.js → vendor-CP0b0YG0.js} +2 -2
  66. phoenix/server/static/assets/{vendor-arizeai-CkvPT67c.js → vendor-arizeai-DTbiPGp6.js} +27 -27
  67. phoenix/server/static/assets/vendor-codemirror-DtdPDzrv.js +15 -0
  68. phoenix/server/static/assets/{vendor-recharts-5jlNaZuF.js → vendor-recharts-A0DA1O99.js} +1 -1
  69. phoenix/server/thread_server.py +2 -2
  70. phoenix/server/types.py +18 -0
  71. phoenix/session/client.py +5 -3
  72. phoenix/session/session.py +2 -2
  73. phoenix/trace/dsl/filter.py +2 -6
  74. phoenix/trace/fixtures.py +17 -23
  75. phoenix/trace/utils.py +23 -0
  76. phoenix/utilities/client.py +116 -0
  77. phoenix/utilities/project.py +1 -1
  78. phoenix/version.py +1 -1
  79. phoenix/server/api/routers/v1/dataset_examples.py +0 -178
  80. phoenix/server/openapi/docs.py +0 -221
  81. phoenix/server/static/assets/components-DeS0YEmv.js +0 -1142
  82. phoenix/server/static/assets/vendor-codemirror-Cqwpwlua.js +0 -12
  83. {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/WHEEL +0 -0
  84. {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/licenses/IP_NOTICE +0 -0
  85. {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,10 @@
1
1
  from datetime import datetime
2
+ from typing import Any, List, Optional
2
3
 
4
+ from fastapi import APIRouter, HTTPException
5
+ from pydantic import Field
3
6
  from sqlalchemy import select
4
7
  from starlette.requests import Request
5
- from starlette.responses import JSONResponse, Response
6
8
  from starlette.status import HTTP_404_NOT_FOUND
7
9
  from strawberry.relay import GlobalID
8
10
 
@@ -10,188 +12,129 @@ from phoenix.db import models
10
12
  from phoenix.db.models import ExperimentRunOutput
11
13
  from phoenix.server.api.types.node import from_global_id_with_expected_type
12
14
 
15
+ from .pydantic_compat import V1RoutesBaseModel
16
+ from .utils import ResponseBody, add_errors_to_responses
13
17
 
14
- async def create_experiment_run(request: Request) -> Response:
15
- """
16
- summary: Create a new experiment run for a specific experiment
17
- operationId: createExperimentRun
18
- tags:
19
- - private
20
- parameters:
21
- - in: path
22
- name: experiment_id
23
- required: true
24
- description: The ID of the experiment for which the run is being created
25
- schema:
26
- type: string
27
- requestBody:
28
- description: Details of the experiment run to be created
29
- required: true
30
- content:
31
- application/json:
32
- schema:
33
- type: object
34
- properties:
35
- dataset_example_id:
36
- type: string
37
- description: The ID of the dataset example used in the experiment run
38
- trace_id:
39
- type: string
40
- description: Optional trace ID for tracking
41
- output:
42
- description: The output of the experiment task
43
- repetition_number:
44
- type: integer
45
- description: The repetition number of the experiment run
46
- start_time:
47
- type: string
48
- format: date-time
49
- description: The start time of the experiment run in ISO format
50
- end_time:
51
- type: string
52
- format: date-time
53
- description: The end time of the experiment run in ISO format
54
- error:
55
- type: string
56
- description: Optional error message if the experiment run encountered an error
57
- nullable: true
58
- required:
59
- - dataset_example_id
60
- - output
61
- - repetition_number
62
- - start_time
63
- - end_time
64
- responses:
65
- 200:
66
- description: Experiment run created successfully
67
- content:
68
- application/json:
69
- schema:
70
- type: object
71
- properties:
72
- data:
73
- type: object
74
- properties:
75
- id:
76
- type: string
77
- description: The ID of the created experiment run
78
- 404:
79
- description: Experiment or DatasetExample not found
80
- """
81
- experiment_gid = GlobalID.from_id(request.path_params["experiment_id"])
18
+ router = APIRouter(tags=["experiments"], include_in_schema=False)
19
+
20
+
21
+ class ExperimentRun(V1RoutesBaseModel):
22
+ dataset_example_id: str = Field(
23
+ description="The ID of the dataset example used in the experiment run"
24
+ )
25
+ output: Any = Field(description="The output of the experiment task")
26
+ repetition_number: int = Field(description="The repetition number of the experiment run")
27
+ start_time: datetime = Field(description="The start time of the experiment run")
28
+ end_time: datetime = Field(description="The end time of the experiment run")
29
+ trace_id: Optional[str] = Field(
30
+ default=None, description="The ID of the corresponding trace (if one exists)"
31
+ )
32
+ error: Optional[str] = Field(
33
+ default=None,
34
+ description="Optional error message if the experiment run encountered an error",
35
+ )
36
+
37
+
38
+ class CreateExperimentRunRequestBody(ExperimentRun):
39
+ pass
40
+
41
+
42
+ class CreateExperimentRunResponseBodyData(V1RoutesBaseModel):
43
+ id: str = Field(description="The ID of the newly created experiment run")
44
+
45
+
46
+ class CreateExperimentResponseBody(ResponseBody[CreateExperimentRunResponseBodyData]):
47
+ pass
48
+
49
+
50
+ @router.post(
51
+ "/experiments/{experiment_id}/runs",
52
+ operation_id="createExperimentRun",
53
+ summary="Create run for an experiment",
54
+ response_description="Experiment run created successfully",
55
+ responses=add_errors_to_responses(
56
+ [
57
+ {
58
+ "status_code": HTTP_404_NOT_FOUND,
59
+ "description": "Experiment or dataset example not found",
60
+ }
61
+ ]
62
+ ),
63
+ )
64
+ async def create_experiment_run(
65
+ request: Request, experiment_id: str, request_body: CreateExperimentRunRequestBody
66
+ ) -> CreateExperimentResponseBody:
67
+ experiment_gid = GlobalID.from_id(experiment_id)
82
68
  try:
83
- experiment_id = from_global_id_with_expected_type(experiment_gid, "Experiment")
69
+ experiment_rowid = from_global_id_with_expected_type(experiment_gid, "Experiment")
84
70
  except ValueError:
85
- return Response(
86
- content=f"Experiment with ID {experiment_gid} does not exist",
71
+ raise HTTPException(
72
+ detail=f"Experiment with ID {experiment_gid} does not exist",
87
73
  status_code=HTTP_404_NOT_FOUND,
88
74
  )
89
75
 
90
- payload = await request.json()
91
-
92
- example_gid = GlobalID.from_id(payload["dataset_example_id"])
76
+ example_gid = GlobalID.from_id(request_body.dataset_example_id)
93
77
  try:
94
78
  dataset_example_id = from_global_id_with_expected_type(example_gid, "DatasetExample")
95
79
  except ValueError:
96
- return Response(
97
- content=f"DatasetExample with ID {example_gid} does not exist",
80
+ raise HTTPException(
81
+ detail=f"DatasetExample with ID {example_gid} does not exist",
98
82
  status_code=HTTP_404_NOT_FOUND,
99
83
  )
100
84
 
101
- trace_id = payload.get("trace_id", None)
102
- task_output = payload["output"]
103
- repetition_number = payload["repetition_number"]
104
- start_time = payload["start_time"]
105
- end_time = payload["end_time"]
106
- error = payload.get("error")
85
+ trace_id = request_body.trace_id
86
+ task_output = request_body.output
87
+ repetition_number = request_body.repetition_number
88
+ start_time = request_body.start_time
89
+ end_time = request_body.end_time
90
+ error = request_body.error
107
91
 
108
92
  async with request.app.state.db() as session:
109
93
  exp_run = models.ExperimentRun(
110
- experiment_id=experiment_id,
94
+ experiment_id=experiment_rowid,
111
95
  dataset_example_id=dataset_example_id,
112
96
  trace_id=trace_id,
113
97
  output=ExperimentRunOutput(task_output=task_output),
114
98
  repetition_number=repetition_number,
115
- start_time=datetime.fromisoformat(start_time),
116
- end_time=datetime.fromisoformat(end_time),
99
+ start_time=start_time,
100
+ end_time=end_time,
117
101
  error=error,
118
102
  )
119
103
  session.add(exp_run)
120
104
  await session.flush()
121
105
  run_gid = GlobalID("ExperimentRun", str(exp_run.id))
122
- return JSONResponse(content={"data": {"id": str(run_gid)}})
123
-
124
-
125
- async def list_experiment_runs(request: Request) -> Response:
126
- """
127
- summary: List all runs for a specific experiment
128
- operationId: listExperimentRuns
129
- tags:
130
- - private
131
- parameters:
132
- - in: path
133
- name: experiment_id
134
- required: true
135
- description: The ID of the experiment to list runs for
136
- schema:
137
- type: string
138
- responses:
139
- 200:
140
- description: Experiment runs retrieved successfully
141
- content:
142
- application/json:
143
- schema:
144
- type: object
145
- properties:
146
- data:
147
- type: array
148
- items:
149
- type: object
150
- properties:
151
- id:
152
- type: string
153
- description: The ID of the experiment run
154
- experiment_id:
155
- type: string
156
- description: The ID of the experiment
157
- dataset_example_id:
158
- type: string
159
- description: The ID of the dataset example
160
- repetition_number:
161
- type: integer
162
- description: The repetition number of the experiment run
163
- start_time:
164
- type: string
165
- format: date-time
166
- description: The start time of the experiment run in ISO format
167
- end_time:
168
- type: string
169
- format: date-time
170
- description: The end time of the experiment run in ISO format
171
- output:
172
- description: The output of the experiment task
173
- error:
174
- type: string
175
- description: Error message if the experiment run encountered an error
176
- trace_id:
177
- type: string
178
- description: Optional trace ID for tracking
179
- 404:
180
- description: Experiment not found
181
- """
182
- experiment_gid = GlobalID.from_id(request.path_params["experiment_id"])
106
+ return CreateExperimentResponseBody(data=CreateExperimentRunResponseBodyData(id=str(run_gid)))
107
+
108
+
109
+ class ListExperimentRunsResponseBody(ResponseBody[List[ExperimentRun]]):
110
+ pass
111
+
112
+
113
+ @router.get(
114
+ "/experiments/{experiment_id}/runs",
115
+ operation_id="listExperimentRuns",
116
+ summary="List runs for an experiment",
117
+ response_description="Experiment runs retrieved successfully",
118
+ responses=add_errors_to_responses(
119
+ [{"status_code": HTTP_404_NOT_FOUND, "description": "Experiment not found"}]
120
+ ),
121
+ )
122
+ async def list_experiment_runs(
123
+ request: Request, experiment_id: str
124
+ ) -> ListExperimentRunsResponseBody:
125
+ experiment_gid = GlobalID.from_id(experiment_id)
183
126
  try:
184
- experiment_id = from_global_id_with_expected_type(experiment_gid, "Experiment")
127
+ experiment_rowid = from_global_id_with_expected_type(experiment_gid, "Experiment")
185
128
  except ValueError:
186
- return Response(
187
- content=f"Experiment with ID {experiment_gid} does not exist",
129
+ raise HTTPException(
130
+ detail=f"Experiment with ID {experiment_gid} does not exist",
188
131
  status_code=HTTP_404_NOT_FOUND,
189
132
  )
190
133
 
191
134
  async with request.app.state.db() as session:
192
135
  experiment_runs = await session.execute(
193
136
  select(models.ExperimentRun)
194
- .where(models.ExperimentRun.experiment_id == experiment_id)
137
+ .where(models.ExperimentRun.experiment_id == experiment_rowid)
195
138
  # order by dataset_example_id to be consistent with `list_dataset_examples`
196
139
  .order_by(models.ExperimentRun.dataset_example_id.asc())
197
140
  )
@@ -202,9 +145,9 @@ async def list_experiment_runs(request: Request) -> Response:
202
145
  experiment_gid = GlobalID("Experiment", str(exp_run.experiment_id))
203
146
  example_gid = GlobalID("DatasetExample", str(exp_run.dataset_example_id))
204
147
  runs.append(
205
- dict(
206
- start_time=exp_run.start_time.isoformat(),
207
- end_time=exp_run.end_time.isoformat(),
148
+ ExperimentRun(
149
+ start_time=exp_run.start_time,
150
+ end_time=exp_run.end_time,
208
151
  experiment_id=str(experiment_gid),
209
152
  dataset_example_id=str(example_gid),
210
153
  repetition_number=exp_run.repetition_number,
@@ -214,4 +157,4 @@ async def list_experiment_runs(request: Request) -> Response:
214
157
  trace_id=exp_run.trace_id,
215
158
  )
216
159
  )
217
- return JSONResponse(content={"data": runs}, status_code=200)
160
+ return ListExperimentRunsResponseBody(data=runs)