arize-phoenix 4.12.0rc1__py3-none-any.whl → 4.12.1rc1__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.
- {arize_phoenix-4.12.0rc1.dist-info → arize_phoenix-4.12.1rc1.dist-info}/METADATA +4 -3
- {arize_phoenix-4.12.0rc1.dist-info → arize_phoenix-4.12.1rc1.dist-info}/RECORD +24 -23
- phoenix/server/api/context.py +3 -7
- phoenix/server/api/openapi/main.py +18 -2
- phoenix/server/api/openapi/schema.py +12 -12
- phoenix/server/api/routers/v1/__init__.py +36 -83
- phoenix/server/api/routers/v1/dataset_examples.py +102 -123
- phoenix/server/api/routers/v1/datasets.py +390 -506
- phoenix/server/api/routers/v1/evaluations.py +73 -66
- phoenix/server/api/routers/v1/experiment_evaluations.py +68 -91
- phoenix/server/api/routers/v1/experiment_runs.py +98 -155
- phoenix/server/api/routers/v1/experiments.py +132 -181
- phoenix/server/api/routers/v1/pydantic_compat.py +78 -0
- phoenix/server/api/routers/v1/spans.py +144 -173
- phoenix/server/api/routers/v1/traces.py +115 -128
- phoenix/server/api/routers/v1/utils.py +95 -0
- phoenix/server/app.py +154 -183
- phoenix/server/templates/index.html +51 -43
- phoenix/server/thread_server.py +2 -2
- phoenix/session/client.py +3 -2
- phoenix/version.py +1 -1
- phoenix/server/openapi/docs.py +0 -221
- {arize_phoenix-4.12.0rc1.dist-info → arize_phoenix-4.12.1rc1.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.12.0rc1.dist-info → arize_phoenix-4.12.1rc1.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.12.0rc1.dist-info → arize_phoenix-4.12.1rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arize-phoenix
|
|
3
|
-
Version: 4.12.
|
|
3
|
+
Version: 4.12.1rc1
|
|
4
4
|
Summary: AI Observability and Evaluation
|
|
5
5
|
Project-URL: Documentation, https://docs.arize.com/phoenix/
|
|
6
6
|
Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
|
|
@@ -22,6 +22,7 @@ Requires-Dist: aiosqlite
|
|
|
22
22
|
Requires-Dist: alembic<2,>=1.3.0
|
|
23
23
|
Requires-Dist: arize-phoenix-evals>=0.13.1
|
|
24
24
|
Requires-Dist: cachetools
|
|
25
|
+
Requires-Dist: fastapi
|
|
25
26
|
Requires-Dist: grpcio
|
|
26
27
|
Requires-Dist: hdbscan>=0.8.33
|
|
27
28
|
Requires-Dist: httpx
|
|
@@ -40,8 +41,8 @@ Requires-Dist: pandas>=1.0
|
|
|
40
41
|
Requires-Dist: protobuf<6.0,>=3.20
|
|
41
42
|
Requires-Dist: psutil
|
|
42
43
|
Requires-Dist: pyarrow
|
|
44
|
+
Requires-Dist: pydantic!=2.0.*,<3,>=1.0
|
|
43
45
|
Requires-Dist: python-multipart
|
|
44
|
-
Requires-Dist: pyyaml
|
|
45
46
|
Requires-Dist: scikit-learn
|
|
46
47
|
Requires-Dist: scipy
|
|
47
48
|
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.4
|
|
@@ -56,9 +57,9 @@ Requires-Dist: uvicorn
|
|
|
56
57
|
Requires-Dist: wrapt
|
|
57
58
|
Provides-Extra: container
|
|
58
59
|
Requires-Dist: opentelemetry-exporter-otlp; extra == 'container'
|
|
60
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi; extra == 'container'
|
|
59
61
|
Requires-Dist: opentelemetry-instrumentation-grpc; extra == 'container'
|
|
60
62
|
Requires-Dist: opentelemetry-instrumentation-sqlalchemy; extra == 'container'
|
|
61
|
-
Requires-Dist: opentelemetry-instrumentation-starlette; extra == 'container'
|
|
62
63
|
Requires-Dist: opentelemetry-proto>=1.12.0; extra == 'container'
|
|
63
64
|
Requires-Dist: opentelemetry-sdk; extra == 'container'
|
|
64
65
|
Requires-Dist: opentelemetry-semantic-conventions; extra == 'container'
|
|
@@ -5,7 +5,7 @@ phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
|
|
|
5
5
|
phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
6
|
phoenix/services.py,sha256=aTxhcOA1pZHB6U-B3TEcp6fqDF5oT0xCUvEUNMZVTUQ,5175
|
|
7
7
|
phoenix/settings.py,sha256=cO-qgis_S27nHirTobYI9hHPfZH18R--WMmxNdsVUwc,273
|
|
8
|
-
phoenix/version.py,sha256=
|
|
8
|
+
phoenix/version.py,sha256=C3HccMWHAx9lnG5eI9Z6SvQ8MEwqYKi1vrfgajsjMXM,26
|
|
9
9
|
phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
|
|
11
11
|
phoenix/core/model.py,sha256=km_a--PBHOuA337ClRw9xqhOHhrUT6Rl9pz_zV0JYkQ,4843
|
|
@@ -60,14 +60,14 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
|
|
|
60
60
|
phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
|
|
61
61
|
phoenix/pointcloud/umap_parameters.py,sha256=3UQSjrysVOvq2V4KNpTMqNqNiK0BsTZnPBHWZ4fyJtQ,1708
|
|
62
62
|
phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
|
-
phoenix/server/app.py,sha256=
|
|
63
|
+
phoenix/server/app.py,sha256=g-UDuF90bnE1nnW6peZY_-1_pplP4ERb2bnD2vERfRg,18758
|
|
64
64
|
phoenix/server/grpc_server.py,sha256=faktLxEtWGlCB1bPR4QwwTsRoQloahKMx0hAWqRGI5s,3379
|
|
65
65
|
phoenix/server/main.py,sha256=dRyODpwkNi_3as14fnZ8LWW_JLWtpXHldRy9SNjNtws,11251
|
|
66
66
|
phoenix/server/prometheus.py,sha256=j9DHB2fERuq_ZKmwVaqR-9wx5WcPPuU1Cm5Bhg5241Y,2996
|
|
67
67
|
phoenix/server/telemetry.py,sha256=T_2OKrxNViAeaANlNspEekg_Y5uZIFWvKAnpz8Aoqvk,2762
|
|
68
|
-
phoenix/server/thread_server.py,sha256=
|
|
68
|
+
phoenix/server/thread_server.py,sha256=RwXQGP_QhGD7le6WB7xEygEEuwBl5Ck_Zo8xGIYGi9M,2135
|
|
69
69
|
phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
70
|
-
phoenix/server/api/context.py,sha256=
|
|
70
|
+
phoenix/server/api/context.py,sha256=G9bmB3h-a4Top6wGAu91ymS-YE6D1DYQyWtPIlZnbgc,2811
|
|
71
71
|
phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
|
|
72
72
|
phoenix/server/api/queries.py,sha256=eq2xHaQF-x4k6AGSY6b6mU2pie9bj-AJML6P2Mr0_DM,19886
|
|
73
73
|
phoenix/server/api/schema.py,sha256=BcxdqO5CSGqpKd-AAJHMjFlzaK9oJA8GJuxmMfcdjn4,434
|
|
@@ -134,19 +134,21 @@ phoenix/server/api/mutations/project_mutations.py,sha256=d_xtYkYfZ5flpVgEkGknKB8
|
|
|
134
134
|
phoenix/server/api/mutations/span_annotations_mutations.py,sha256=Pfaq4y-FGskPit4z_9GvsyWeBwK1g3CDi2UhGKxyjFE,4973
|
|
135
135
|
phoenix/server/api/mutations/trace_annotations_mutations.py,sha256=4xm-zg8PEjwjfVRsWzBD_iiOS94JI6UYtK3fV2dtA4M,5013
|
|
136
136
|
phoenix/server/api/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
137
|
-
phoenix/server/api/openapi/main.py,sha256=
|
|
138
|
-
phoenix/server/api/openapi/schema.py,sha256=
|
|
137
|
+
phoenix/server/api/openapi/main.py,sha256=KNutA_7AvV_WlGX8cOkvvDujcJKQ7AD1HT6rTpCpR8A,616
|
|
138
|
+
phoenix/server/api/openapi/schema.py,sha256=oVZoflWMfzOrLKMIrjr3iLnJ13rmN-t_DOe9g6KoN5s,471
|
|
139
139
|
phoenix/server/api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
140
140
|
phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
|
|
141
|
-
phoenix/server/api/routers/v1/__init__.py,sha256=
|
|
142
|
-
phoenix/server/api/routers/v1/dataset_examples.py,sha256=
|
|
143
|
-
phoenix/server/api/routers/v1/datasets.py,sha256
|
|
144
|
-
phoenix/server/api/routers/v1/evaluations.py,sha256=
|
|
145
|
-
phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=
|
|
146
|
-
phoenix/server/api/routers/v1/experiment_runs.py,sha256=
|
|
147
|
-
phoenix/server/api/routers/v1/experiments.py,sha256=
|
|
148
|
-
phoenix/server/api/routers/v1/
|
|
149
|
-
phoenix/server/api/routers/v1/
|
|
141
|
+
phoenix/server/api/routers/v1/__init__.py,sha256=nb49zcOdAi3DSGuC9gUubN9Yri-o7-WFdlGak4jGuFw,1462
|
|
142
|
+
phoenix/server/api/routers/v1/dataset_examples.py,sha256=6ep0Trojax_NbwpCOlofNG9p4xMu6VJgZPMjkAUJQOA,5627
|
|
143
|
+
phoenix/server/api/routers/v1/datasets.py,sha256=-QzKYNV0DCWfw3D_k_KO1iF_PY57fNAvx7NGznNWt0g,32294
|
|
144
|
+
phoenix/server/api/routers/v1/evaluations.py,sha256=xNhS_VOA8S0BIijb1NSAOW677PzLOVl39Fl00g_T7Vo,9553
|
|
145
|
+
phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=xoVhU71U3c1QJSXiKsAa4yiH-UAqkyVc7uKPyIHjrYY,4682
|
|
146
|
+
phoenix/server/api/routers/v1/experiment_runs.py,sha256=7qvLYgqH58nxqhTnJ0hPf0PBfVmZnbxquodSnEGoQxk,6059
|
|
147
|
+
phoenix/server/api/routers/v1/experiments.py,sha256=iS2IBgR7WX1rgYlO90b2sleVU-C0y57RgI5TslCYMFw,9812
|
|
148
|
+
phoenix/server/api/routers/v1/pydantic_compat.py,sha256=FeK8oe2brqu-djsoqRxiKL4tw5cHmi89OHVfCFxYsAo,2890
|
|
149
|
+
phoenix/server/api/routers/v1/spans.py,sha256=yMs3Sm6s6JLV2nlmpnQ-ZJ_In0aTY5M3uOlLi-Wzb9c,8967
|
|
150
|
+
phoenix/server/api/routers/v1/traces.py,sha256=Zl_hGHd-4rA0tXegH_GVoN9Ij84vbPB8oHu28fzGHA8,8029
|
|
151
|
+
phoenix/server/api/routers/v1/utils.py,sha256=xvl2v-BKUkqmFVMmgmmWGFKuRBTrUdoiAeT3mCYEE68,3086
|
|
150
152
|
phoenix/server/api/types/Annotation.py,sha256=7Ym7iuVcbwHlw2yIRylz4nATAF_Cm-Z17qcjiooj1cc,751
|
|
151
153
|
phoenix/server/api/types/AnnotatorKind.py,sha256=rPgGdbN1Gvc109sGQ_ZH-gfJbp93V9wlarzTEJNtUwI,236
|
|
152
154
|
phoenix/server/api/types/Cluster.py,sha256=ac4YfT1OH3xLVmex7EUmB6b9IpULnhLTt554LR0jglE,5689
|
|
@@ -202,7 +204,6 @@ phoenix/server/api/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
202
204
|
phoenix/server/api/types/node.py,sha256=V0Xh9U4cGkz3iMg-vzEXtcs6cumU29JFPiU-JuGzjWI,848
|
|
203
205
|
phoenix/server/api/types/pagination.py,sha256=PcaJ0s4exsTKgCZC4aFm1cgZNrGpHSdo6PbkWzPcweg,9077
|
|
204
206
|
phoenix/server/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
205
|
-
phoenix/server/openapi/docs.py,sha256=fTb9q2oOSKC5bLVQy2Dsg3Bs0mGkCOKX1ypX7731sE0,7044
|
|
206
207
|
phoenix/server/static/apple-touch-icon-114x114.png,sha256=xtFVXAYQnJkpUApg2D1hltSTuyO4Is4sD4A0ZkikiVU,9486
|
|
207
208
|
phoenix/server/static/apple-touch-icon-120x120.png,sha256=iqZVAk634BbjJMozA8aHYyw15JjhIlIrG41FA2DFFaE,9957
|
|
208
209
|
phoenix/server/static/apple-touch-icon-144x144.png,sha256=VgARtkHKoU8zikb3_G83h_cb02kpPcoJqO78yRh1AfU,10047
|
|
@@ -224,9 +225,9 @@ phoenix/server/static/assets/vendor-codemirror-CrdxOlMs.js,sha256=KMJHeNKQyBjzBz
|
|
|
224
225
|
phoenix/server/static/assets/vendor-recharts-PKRvByVe.js,sha256=Uul1I5FtZKipkx1ku4y2OqWd86GiO4aB4dA9o_LJbvM,282859
|
|
225
226
|
phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJBFR2RZO_xxeMXrqDp0AszZqHY,620972
|
|
226
227
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
227
|
-
phoenix/server/templates/index.html,sha256=
|
|
228
|
+
phoenix/server/templates/index.html,sha256=gVpjB8pCMiubdMh2DA9mTCtV5AVTXJH_9u5PmG2t7Vk,4238
|
|
228
229
|
phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
229
|
-
phoenix/session/client.py,sha256=
|
|
230
|
+
phoenix/session/client.py,sha256=niTRnsmLb6OdrwNvFNgVqrkft5ZRpYYZoWq4WhjS_AI,32555
|
|
230
231
|
phoenix/session/data_extractor.py,sha256=gkEM3WWZAlWGMfRgQopAQlid4cSi6GNco-sdrGir0qc,2788
|
|
231
232
|
phoenix/session/evaluation.py,sha256=aKeV8UVOyq3b7CYOwt3cWuLz0xzvMjX7vlEPILJ_fcs,5311
|
|
232
233
|
phoenix/session/session.py,sha256=1ZGR0pBmah8bqX353MDf4sq7XuK904EfxNLo0B9z_sU,26714
|
|
@@ -266,8 +267,8 @@ phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,
|
|
|
266
267
|
phoenix/utilities/project.py,sha256=qWsvKnG1oKhOFUowXf9qiOL2ia7jaFe_ijFFHEt8GJo,431
|
|
267
268
|
phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
|
|
268
269
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
269
|
-
arize_phoenix-4.12.
|
|
270
|
-
arize_phoenix-4.12.
|
|
271
|
-
arize_phoenix-4.12.
|
|
272
|
-
arize_phoenix-4.12.
|
|
273
|
-
arize_phoenix-4.12.
|
|
270
|
+
arize_phoenix-4.12.1rc1.dist-info/METADATA,sha256=9EuJLBvIpbylubhJ6sCVH2zk-_klCyHiBYHqyGdAB5M,11494
|
|
271
|
+
arize_phoenix-4.12.1rc1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
272
|
+
arize_phoenix-4.12.1rc1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
273
|
+
arize_phoenix-4.12.1rc1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
274
|
+
arize_phoenix-4.12.1rc1.dist-info/RECORD,,
|
phoenix/server/api/context.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import AsyncContextManager, Callable, Optional
|
|
4
|
+
from typing import AsyncContextManager, Callable, Optional
|
|
5
5
|
|
|
6
6
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
7
|
-
from
|
|
8
|
-
from starlette.responses import Response
|
|
9
|
-
from starlette.websockets import WebSocket
|
|
7
|
+
from strawberry.fastapi import BaseContext
|
|
10
8
|
from typing_extensions import TypeAlias
|
|
11
9
|
|
|
12
10
|
from phoenix.core.model_schema import Model
|
|
@@ -67,9 +65,7 @@ ProjectRowId: TypeAlias = int
|
|
|
67
65
|
|
|
68
66
|
|
|
69
67
|
@dataclass
|
|
70
|
-
class Context:
|
|
71
|
-
request: Union[Request, WebSocket]
|
|
72
|
-
response: Optional[Response]
|
|
68
|
+
class Context(BaseContext):
|
|
73
69
|
db: Callable[[], AsyncContextManager[AsyncSession]]
|
|
74
70
|
data_loaders: DataLoaders
|
|
75
71
|
cache_for_dataloaders: Optional[CacheForDataLoaders]
|
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from argparse import ArgumentParser
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
|
|
1
5
|
from .schema import get_openapi_schema
|
|
2
6
|
|
|
3
7
|
if __name__ == "__main__":
|
|
4
|
-
|
|
8
|
+
parser = ArgumentParser()
|
|
9
|
+
parser.add_argument(
|
|
10
|
+
"--compress",
|
|
11
|
+
action="store_true",
|
|
12
|
+
help="Whether to output a compressed version of the OpenAPI schema",
|
|
13
|
+
)
|
|
14
|
+
args = parser.parse_args()
|
|
5
15
|
|
|
6
|
-
|
|
16
|
+
indent: Optional[int] = None
|
|
17
|
+
separator: Optional[Tuple[str, str]] = None
|
|
18
|
+
if args.compress:
|
|
19
|
+
separator = (",", ":")
|
|
20
|
+
else:
|
|
21
|
+
indent = 2
|
|
22
|
+
print(json.dumps(get_openapi_schema(), indent=indent, separators=separator))
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any, Dict
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from fastapi.openapi.utils import get_openapi
|
|
4
4
|
|
|
5
|
-
from phoenix.server.api.routers.v1 import
|
|
5
|
+
from phoenix.server.api.routers.v1 import REST_API_VERSION
|
|
6
|
+
from phoenix.server.api.routers.v1 import router as v1_router
|
|
6
7
|
|
|
7
|
-
OPENAPI_SCHEMA_GENERATOR = SchemaGenerator(
|
|
8
|
-
{"openapi": "3.0.0", "info": {"title": "Arize-Phoenix API", "version": "1.0"}}
|
|
9
|
-
)
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
def get_openapi_schema() -> Dict[str, Any]:
|
|
10
|
+
return get_openapi(
|
|
11
|
+
title="Arize-Phoenix REST API",
|
|
12
|
+
version=REST_API_VERSION,
|
|
13
|
+
openapi_version="3.1.0",
|
|
14
|
+
description="Schema for Arize-Phoenix REST API",
|
|
15
|
+
routes=v1_router.routes,
|
|
16
|
+
)
|
|
@@ -1,89 +1,42 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
import wrapt
|
|
4
|
-
from starlette import routing
|
|
5
|
-
from starlette.requests import Request
|
|
6
|
-
from starlette.responses import Response
|
|
1
|
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
7
2
|
from starlette.status import HTTP_403_FORBIDDEN
|
|
8
3
|
|
|
9
|
-
from . import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
from .dataset_examples import list_dataset_examples
|
|
19
|
-
|
|
4
|
+
from .datasets import router as datasets_router
|
|
5
|
+
from .evaluations import router as evaluations_router
|
|
6
|
+
from .experiment_evaluations import router as experiment_evaluations_router
|
|
7
|
+
from .experiment_runs import router as experiment_runs_router
|
|
8
|
+
from .experiments import router as experiments_router
|
|
9
|
+
from .spans import router as spans_router
|
|
10
|
+
from .traces import router as traces_router
|
|
11
|
+
from .utils import add_errors_to_responses
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
async def forbid_if_readonly(
|
|
23
|
-
wrapped: Callable[[Request], Awaitable[Response]],
|
|
24
|
-
_: Any,
|
|
25
|
-
args: Tuple[Request],
|
|
26
|
-
kwargs: Mapping[str, Any],
|
|
27
|
-
) -> Response:
|
|
28
|
-
request, *_ = args
|
|
29
|
-
if request.app.state.read_only:
|
|
30
|
-
return Response(status_code=HTTP_403_FORBIDDEN)
|
|
31
|
-
return await wrapped(*args, **kwargs)
|
|
13
|
+
REST_API_VERSION = "1.0"
|
|
32
14
|
|
|
33
15
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Route(
|
|
53
|
-
"/v1/datasets/{id:str}/jsonl/openai_ft",
|
|
54
|
-
datasets.get_dataset_jsonl_openai_ft,
|
|
55
|
-
methods=["GET"],
|
|
56
|
-
),
|
|
57
|
-
Route(
|
|
58
|
-
"/v1/datasets/{id:str}/jsonl/openai_evals",
|
|
59
|
-
datasets.get_dataset_jsonl_openai_evals,
|
|
60
|
-
methods=["GET"],
|
|
61
|
-
),
|
|
62
|
-
Route("/v1/datasets/{id:str}/examples", list_dataset_examples, methods=["GET"]),
|
|
63
|
-
Route("/v1/datasets/{id:str}/versions", datasets.get_dataset_versions, methods=["GET"]),
|
|
64
|
-
Route(
|
|
65
|
-
"/v1/datasets/{dataset_id:str}/experiments",
|
|
66
|
-
experiments.create_experiment,
|
|
67
|
-
methods=["POST"],
|
|
68
|
-
),
|
|
69
|
-
Route(
|
|
70
|
-
"/v1/experiments/{experiment_id:str}",
|
|
71
|
-
experiments.read_experiment,
|
|
72
|
-
methods=["GET"],
|
|
73
|
-
),
|
|
74
|
-
Route(
|
|
75
|
-
"/v1/experiments/{experiment_id:str}/runs",
|
|
76
|
-
experiment_runs.create_experiment_run,
|
|
77
|
-
methods=["POST"],
|
|
78
|
-
),
|
|
79
|
-
Route(
|
|
80
|
-
"/v1/experiments/{experiment_id:str}/runs",
|
|
81
|
-
experiment_runs.list_experiment_runs,
|
|
82
|
-
methods=["GET"],
|
|
83
|
-
),
|
|
84
|
-
Route(
|
|
85
|
-
"/v1/experiment_evaluations",
|
|
86
|
-
experiment_evaluations.upsert_experiment_evaluation,
|
|
87
|
-
methods=["POST"],
|
|
16
|
+
async def prevent_access_in_read_only_mode(request: Request) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Prevents access to the REST API in read-only mode.
|
|
19
|
+
"""
|
|
20
|
+
if request.app.state.read_only:
|
|
21
|
+
raise HTTPException(
|
|
22
|
+
detail="The Phoenix REST API is disabled in read-only mode.",
|
|
23
|
+
status_code=HTTP_403_FORBIDDEN,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
router = APIRouter(
|
|
28
|
+
prefix="/v1",
|
|
29
|
+
dependencies=[Depends(prevent_access_in_read_only_mode)],
|
|
30
|
+
responses=add_errors_to_responses(
|
|
31
|
+
[
|
|
32
|
+
HTTP_403_FORBIDDEN # adds a 403 response to each route in the generated OpenAPI schema
|
|
33
|
+
]
|
|
88
34
|
),
|
|
89
|
-
|
|
35
|
+
)
|
|
36
|
+
router.include_router(datasets_router)
|
|
37
|
+
router.include_router(experiments_router)
|
|
38
|
+
router.include_router(experiment_runs_router)
|
|
39
|
+
router.include_router(experiment_evaluations_router)
|
|
40
|
+
router.include_router(traces_router)
|
|
41
|
+
router.include_router(spans_router)
|
|
42
|
+
router.include_router(evaluations_router)
|
|
@@ -1,178 +1,157 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter, HTTPException, Path, Query
|
|
1
5
|
from sqlalchemy import and_, func, select
|
|
2
6
|
from starlette.requests import Request
|
|
3
|
-
from starlette.responses import JSONResponse, Response
|
|
4
7
|
from starlette.status import HTTP_404_NOT_FOUND
|
|
5
8
|
from strawberry.relay import GlobalID
|
|
6
9
|
|
|
7
|
-
from phoenix.db.models import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
required:
|
|
67
|
-
- id
|
|
68
|
-
- input
|
|
69
|
-
- output
|
|
70
|
-
- metadata
|
|
71
|
-
- updated_at
|
|
72
|
-
required:
|
|
73
|
-
- dataset_id
|
|
74
|
-
- version_id
|
|
75
|
-
- examples
|
|
76
|
-
403:
|
|
77
|
-
description: Forbidden
|
|
78
|
-
404:
|
|
79
|
-
description: Dataset does not exist.
|
|
80
|
-
"""
|
|
81
|
-
dataset_id = GlobalID.from_id(request.path_params["id"])
|
|
82
|
-
raw_version_id = request.query_params.get("version_id")
|
|
83
|
-
version_id = GlobalID.from_id(raw_version_id) if raw_version_id else None
|
|
84
|
-
|
|
85
|
-
if (dataset_type := dataset_id.type_name) != "Dataset":
|
|
86
|
-
return Response(
|
|
87
|
-
content=f"ID {dataset_id} refers to a {dataset_type}", status_code=HTTP_404_NOT_FOUND
|
|
10
|
+
from phoenix.db.models import (
|
|
11
|
+
Dataset as ORMDataset,
|
|
12
|
+
)
|
|
13
|
+
from phoenix.db.models import (
|
|
14
|
+
DatasetExample as ORMDatasetExample,
|
|
15
|
+
)
|
|
16
|
+
from phoenix.db.models import (
|
|
17
|
+
DatasetExampleRevision as ORMDatasetExampleRevision,
|
|
18
|
+
)
|
|
19
|
+
from phoenix.db.models import (
|
|
20
|
+
DatasetVersion as ORMDatasetVersion,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .pydantic_compat import V1RoutesBaseModel
|
|
24
|
+
from .utils import ResponseBody, add_errors_to_responses
|
|
25
|
+
|
|
26
|
+
router = APIRouter(tags=["datasets"])
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DatasetExample(V1RoutesBaseModel):
|
|
30
|
+
id: str
|
|
31
|
+
input: Dict[str, Any]
|
|
32
|
+
output: Dict[str, Any]
|
|
33
|
+
metadata: Dict[str, Any]
|
|
34
|
+
updated_at: datetime
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ListDatasetExamplesData(V1RoutesBaseModel):
|
|
38
|
+
dataset_id: str
|
|
39
|
+
version_id: str
|
|
40
|
+
examples: List[DatasetExample]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ListDatasetExamplesResponseBody(ResponseBody[ListDatasetExamplesData]):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@router.get(
|
|
48
|
+
"/datasets/{id}/examples",
|
|
49
|
+
operation_id="getDatasetExamples",
|
|
50
|
+
summary="Get examples from a dataset",
|
|
51
|
+
responses=add_errors_to_responses([HTTP_404_NOT_FOUND]),
|
|
52
|
+
)
|
|
53
|
+
async def get_dataset_examples(
|
|
54
|
+
request: Request,
|
|
55
|
+
id: str = Path(description="The ID of the dataset"),
|
|
56
|
+
version_id: Optional[str] = Query(
|
|
57
|
+
default=None,
|
|
58
|
+
description=(
|
|
59
|
+
"The ID of the dataset version " "(if omitted, returns data from the latest version)"
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
) -> ListDatasetExamplesResponseBody:
|
|
63
|
+
dataset_gid = GlobalID.from_id(id)
|
|
64
|
+
version_gid = GlobalID.from_id(version_id) if version_id else None
|
|
65
|
+
|
|
66
|
+
if (dataset_type := dataset_gid.type_name) != "Dataset":
|
|
67
|
+
raise HTTPException(
|
|
68
|
+
detail=f"ID {dataset_gid} refers to a {dataset_type}", status_code=HTTP_404_NOT_FOUND
|
|
88
69
|
)
|
|
89
70
|
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
71
|
+
if version_gid and (version_type := version_gid.type_name) != "DatasetVersion":
|
|
72
|
+
raise HTTPException(
|
|
73
|
+
detail=f"ID {version_gid} refers to a {version_type}", status_code=HTTP_404_NOT_FOUND
|
|
93
74
|
)
|
|
94
75
|
|
|
95
76
|
async with request.app.state.db() as session:
|
|
96
77
|
if (
|
|
97
78
|
resolved_dataset_id := await session.scalar(
|
|
98
|
-
select(
|
|
79
|
+
select(ORMDataset.id).where(ORMDataset.id == int(dataset_gid.node_id))
|
|
99
80
|
)
|
|
100
81
|
) is None:
|
|
101
|
-
|
|
102
|
-
|
|
82
|
+
raise HTTPException(
|
|
83
|
+
detail=f"No dataset with id {dataset_gid} can be found.",
|
|
103
84
|
status_code=HTTP_404_NOT_FOUND,
|
|
104
85
|
)
|
|
105
86
|
|
|
106
87
|
# Subquery to find the maximum created_at for each dataset_example_id
|
|
107
88
|
# timestamp tiebreaks are resolved by the largest id
|
|
108
89
|
partial_subquery = select(
|
|
109
|
-
func.max(
|
|
110
|
-
).group_by(
|
|
90
|
+
func.max(ORMDatasetExampleRevision.id).label("max_id"),
|
|
91
|
+
).group_by(ORMDatasetExampleRevision.dataset_example_id)
|
|
111
92
|
|
|
112
|
-
if
|
|
93
|
+
if version_gid:
|
|
113
94
|
if (
|
|
114
95
|
resolved_version_id := await session.scalar(
|
|
115
|
-
select(
|
|
96
|
+
select(ORMDatasetVersion.id).where(
|
|
116
97
|
and_(
|
|
117
|
-
|
|
118
|
-
|
|
98
|
+
ORMDatasetVersion.dataset_id == resolved_dataset_id,
|
|
99
|
+
ORMDatasetVersion.id == int(version_gid.node_id),
|
|
119
100
|
)
|
|
120
101
|
)
|
|
121
102
|
)
|
|
122
103
|
) is None:
|
|
123
|
-
|
|
124
|
-
|
|
104
|
+
raise HTTPException(
|
|
105
|
+
detail=f"No dataset version with id {version_id} can be found.",
|
|
125
106
|
status_code=HTTP_404_NOT_FOUND,
|
|
126
107
|
)
|
|
127
108
|
# if a version_id is provided, filter the subquery to only include revisions from that
|
|
128
109
|
partial_subquery = partial_subquery.filter(
|
|
129
|
-
|
|
110
|
+
ORMDatasetExampleRevision.dataset_version_id <= resolved_version_id
|
|
130
111
|
)
|
|
131
112
|
else:
|
|
132
113
|
if (
|
|
133
114
|
resolved_version_id := await session.scalar(
|
|
134
|
-
select(func.max(
|
|
135
|
-
|
|
115
|
+
select(func.max(ORMDatasetVersion.id)).where(
|
|
116
|
+
ORMDatasetVersion.dataset_id == resolved_dataset_id
|
|
136
117
|
)
|
|
137
118
|
)
|
|
138
119
|
) is None:
|
|
139
|
-
|
|
140
|
-
|
|
120
|
+
raise HTTPException(
|
|
121
|
+
detail="Dataset has no versions.",
|
|
141
122
|
status_code=HTTP_404_NOT_FOUND,
|
|
142
123
|
)
|
|
143
124
|
|
|
144
125
|
subquery = partial_subquery.subquery()
|
|
145
126
|
# Query for the most recent example revisions that are not deleted
|
|
146
127
|
query = (
|
|
147
|
-
select(
|
|
128
|
+
select(ORMDatasetExample, ORMDatasetExampleRevision)
|
|
148
129
|
.join(
|
|
149
|
-
|
|
150
|
-
|
|
130
|
+
ORMDatasetExampleRevision,
|
|
131
|
+
ORMDatasetExample.id == ORMDatasetExampleRevision.dataset_example_id,
|
|
151
132
|
)
|
|
152
133
|
.join(
|
|
153
134
|
subquery,
|
|
154
|
-
(subquery.c.max_id ==
|
|
135
|
+
(subquery.c.max_id == ORMDatasetExampleRevision.id),
|
|
155
136
|
)
|
|
156
|
-
.filter(
|
|
157
|
-
.filter(
|
|
158
|
-
.order_by(
|
|
137
|
+
.filter(ORMDatasetExample.dataset_id == resolved_dataset_id)
|
|
138
|
+
.filter(ORMDatasetExampleRevision.revision_kind != "DELETE")
|
|
139
|
+
.order_by(ORMDatasetExample.id.asc())
|
|
159
140
|
)
|
|
160
141
|
examples = [
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
142
|
+
DatasetExample(
|
|
143
|
+
id=str(GlobalID("DatasetExample", str(example.id))),
|
|
144
|
+
input=revision.input,
|
|
145
|
+
output=revision.output,
|
|
146
|
+
metadata=revision.metadata_,
|
|
147
|
+
updated_at=revision.created_at,
|
|
148
|
+
)
|
|
168
149
|
async for example, revision in await session.stream(query)
|
|
169
150
|
]
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
"
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
}
|
|
151
|
+
return ListDatasetExamplesResponseBody(
|
|
152
|
+
data=ListDatasetExamplesData(
|
|
153
|
+
dataset_id=str(GlobalID("Dataset", str(resolved_dataset_id))),
|
|
154
|
+
version_id=str(GlobalID("DatasetVersion", str(resolved_version_id))),
|
|
155
|
+
examples=examples,
|
|
156
|
+
)
|
|
178
157
|
)
|