arize-phoenix 11.30.0__py3-none-any.whl → 11.31.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.
- {arize_phoenix-11.30.0.dist-info → arize_phoenix-11.31.0.dist-info}/METADATA +17 -17
- {arize_phoenix-11.30.0.dist-info → arize_phoenix-11.31.0.dist-info}/RECORD +24 -23
- phoenix/db/types/trace_retention.py +1 -1
- phoenix/server/api/dataloaders/document_evaluations.py +6 -9
- phoenix/server/api/routers/v1/annotations.py +128 -5
- phoenix/server/api/routers/v1/documents.py +47 -79
- phoenix/server/api/routers/v1/spans.py +2 -48
- phoenix/server/api/routers/v1/traces.py +19 -55
- phoenix/server/api/types/Dataset.py +8 -66
- phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
- phoenix/server/api/types/DocumentAnnotation.py +92 -0
- phoenix/server/api/types/Span.py +8 -2
- phoenix/server/api/types/TraceAnnotation.py +8 -5
- phoenix/server/cost_tracking/model_cost_manifest.json +91 -0
- phoenix/server/static/.vite/manifest.json +9 -9
- phoenix/server/static/assets/{components-BBwXqJXQ.js → components-BjW5gAwL.js} +1 -1
- phoenix/server/static/assets/{index-C_gU3x10.js → index-3OI8VV_W.js} +1 -1
- phoenix/server/static/assets/{pages-YmQb55Uo.js → pages-CQfUODtD.js} +57 -56
- phoenix/trace/projects.py +6 -0
- phoenix/version.py +1 -1
- phoenix/server/api/types/Evaluation.py +0 -40
- {arize_phoenix-11.30.0.dist-info → arize_phoenix-11.31.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-11.30.0.dist-info → arize_phoenix-11.31.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-11.30.0.dist-info → arize_phoenix-11.31.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-11.30.0.dist-info → arize_phoenix-11.31.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arize-phoenix
|
|
3
|
-
Version: 11.
|
|
3
|
+
Version: 11.31.0
|
|
4
4
|
Summary: AI Observability and Evaluation
|
|
5
5
|
Project-URL: Documentation, https://arize.com/docs/phoenix/
|
|
6
6
|
Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
|
|
@@ -201,14 +201,14 @@ The `arize-phoenix` package includes the entire Phoenix platfom. However if you
|
|
|
201
201
|
|
|
202
202
|
### Subpackages
|
|
203
203
|
|
|
204
|
-
| Package |
|
|
205
|
-
| --------------------------------------------------------------------------------------------------- |
|
|
206
|
-
| [arize-phoenix-otel](https://github.com/Arize-ai/phoenix/tree/main/packages/phoenix-otel) |
|
|
207
|
-
| [arize-phoenix-client](https://github.com/Arize-ai/phoenix/tree/main/packages/phoenix-client) |
|
|
208
|
-
| [arize-phoenix-evals](https://github.com/Arize-ai/phoenix/tree/main/packages/phoenix-evals) |
|
|
209
|
-
| [@arizeai/phoenix-client](https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-client) |
|
|
210
|
-
| [@arizeai/phoenix-evals](https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-evals) |
|
|
211
|
-
| [@arizeai/phoenix-mcp](https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-mcp) |
|
|
204
|
+
| Package | Version & Docs | Description |
|
|
205
|
+
| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
206
|
+
| [arize-phoenix-otel](https://github.com/Arize-ai/phoenix/tree/main/packages/phoenix-otel) | [](https://pypi.org/project/arize-phoenix-otel/) [](https://arize-phoenix.readthedocs.io/projects/otel/en/latest/index.html) | Provides a lightweight wrapper around OpenTelemetry primitives with Phoenix-aware defaults |
|
|
207
|
+
| [arize-phoenix-client](https://github.com/Arize-ai/phoenix/tree/main/packages/phoenix-client) | [](https://pypi.org/project/arize-phoenix-client/) [](https://arize-phoenix.readthedocs.io/projects/client/en/latest/index.html) | Lightweight client for interacting with the Phoenix server via its OpenAPI REST interface |
|
|
208
|
+
| [arize-phoenix-evals](https://github.com/Arize-ai/phoenix/tree/main/packages/phoenix-evals) | [](https://pypi.org/project/arize-phoenix-evals/) [](https://arize-phoenix.readthedocs.io/projects/evals/en/latest/index.html) | Tooling to evaluate LLM applications including RAG relevance, answer relevance, and more |
|
|
209
|
+
| [@arizeai/phoenix-client](https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-client) | [](https://www.npmjs.com/package/@arizeai/phoenix-client) [](https://arize-ai.github.io/phoenix/) | Client for the Arize Phoenix API |
|
|
210
|
+
| [@arizeai/phoenix-evals](https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-evals) | [](https://www.npmjs.com/package/@arizeai/phoenix-evals) [](https://arize-ai.github.io/phoenix/) | TypeScript evaluation library for LLM applications (alpha release) |
|
|
211
|
+
| [@arizeai/phoenix-mcp](https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-mcp) | [](https://www.npmjs.com/package/@arizeai/phoenix-mcp) [](./js/packages/phoenix-mcp/README.md) | MCP server implementation for Arize Phoenix providing unified interface to Phoenix's capabilities |
|
|
212
212
|
|
|
213
213
|
## Tracing Integrations
|
|
214
214
|
|
|
@@ -256,17 +256,17 @@ Phoenix is built on top of OpenTelemetry and is vendor, language, and framework
|
|
|
256
256
|
| Integration | Package | Version Badge |
|
|
257
257
|
| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
258
258
|
| [LangChain4j](https://github.com/Arize-ai/openinference/tree/main/java/instrumentation/openinference-instrumentation-langchain4j) | `openinference-instrumentation-langchain4j` | [](https://central.sonatype.com/artifact/com.arize/openinference-instrumentation-langchain4j) |
|
|
259
|
-
| [SpringAI](https://central.sonatype.com/artifact/com.arize/openinference-instrumentation-springAI)
|
|
259
|
+
| [SpringAI](https://central.sonatype.com/artifact/com.arize/openinference-instrumentation-springAI) | `openinference-instrumentation-springAI` | [](https://central.sonatype.com/artifact/com.arize/openinference-instrumentation-springAI) |
|
|
260
260
|
|
|
261
261
|
### Platforms
|
|
262
262
|
|
|
263
|
-
| Platform | Description | Docs
|
|
264
|
-
| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
265
|
-
| [BeeAI](https://docs.beeai.dev/observability/agents-traceability) | AI agent framework with built-in observability | [Integration Guide](https://docs.beeai.dev/observability/agents-traceability)
|
|
266
|
-
| [Dify](https://docs.dify.ai/en/guides/monitoring/integrate-external-ops-tools/integrate-phoenix) | Open-source LLM app development platform | [Integration Guide](https://docs.dify.ai/en/guides/monitoring/integrate-external-ops-tools/integrate-phoenix)
|
|
267
|
-
| [Envoy AI Gateway](https://github.com/envoyproxy/ai-gateway)
|
|
268
|
-
| [LangFlow](https://arize.com/docs/phoenix/tracing/integrations-tracing/langflow) | Visual framework for building multi-agent and RAG applications | [Integration Guide](https://arize.com/docs/phoenix/tracing/integrations-tracing/langflow)
|
|
269
|
-
| [LiteLLM Proxy](https://docs.litellm.ai/docs/observability/phoenix_integration#using-with-litellm-proxy) | Proxy server for LLMs | [Integration Guide](https://docs.litellm.ai/docs/observability/phoenix_integration#using-with-litellm-proxy)
|
|
263
|
+
| Platform | Description | Docs |
|
|
264
|
+
| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
265
|
+
| [BeeAI](https://docs.beeai.dev/observability/agents-traceability) | AI agent framework with built-in observability | [Integration Guide](https://docs.beeai.dev/observability/agents-traceability) |
|
|
266
|
+
| [Dify](https://docs.dify.ai/en/guides/monitoring/integrate-external-ops-tools/integrate-phoenix) | Open-source LLM app development platform | [Integration Guide](https://docs.dify.ai/en/guides/monitoring/integrate-external-ops-tools/integrate-phoenix) |
|
|
267
|
+
| [Envoy AI Gateway](https://github.com/envoyproxy/ai-gateway) | AI Gateway built on Envoy Proxy for AI workloads | [Integration Guide](https://github.com/envoyproxy/ai-gateway/tree/main/cmd/aigw#opentelemetry-setup-with-phoenix) |
|
|
268
|
+
| [LangFlow](https://arize.com/docs/phoenix/tracing/integrations-tracing/langflow) | Visual framework for building multi-agent and RAG applications | [Integration Guide](https://arize.com/docs/phoenix/tracing/integrations-tracing/langflow) |
|
|
269
|
+
| [LiteLLM Proxy](https://docs.litellm.ai/docs/observability/phoenix_integration#using-with-litellm-proxy) | Proxy server for LLMs | [Integration Guide](https://docs.litellm.ai/docs/observability/phoenix_integration#using-with-litellm-proxy) |
|
|
270
270
|
|
|
271
271
|
## Community
|
|
272
272
|
|
|
@@ -6,7 +6,7 @@ phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
|
|
|
6
6
|
phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
7
7
|
phoenix/services.py,sha256=ngkyKGVatX3cO2WJdo2hKdaVKP-xJCMvqthvga6kJss,5196
|
|
8
8
|
phoenix/settings.py,sha256=2kHfT3BNOVd4dAO1bq-syEQbHSG8oX2-7NhOwK2QREk,896
|
|
9
|
-
phoenix/version.py,sha256=
|
|
9
|
+
phoenix/version.py,sha256=np-y_W3enAyHJplYVsNoeOIf4b0r7heOgEKw1jPc6io,24
|
|
10
10
|
phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
|
|
12
12
|
phoenix/core/model.py,sha256=qBFraOtmwCCnWJltKNP18DDG0mULXigytlFsa6YOz6k,4837
|
|
@@ -56,7 +56,7 @@ phoenix/db/types/db_models.py,sha256=nMSd9gWHwObnVO3_slztlHqoeh04czS-Jxu-omS6M6E
|
|
|
56
56
|
phoenix/db/types/identifier.py,sha256=Opr3_1di6e5ncrBDn30WfBSr-jN_VGBnkkA4BMuSoyc,244
|
|
57
57
|
phoenix/db/types/model_provider.py,sha256=zKYGcEQqbAxtPwnq5dL0fYPgDC8nrh_ABLBMR94___4,237
|
|
58
58
|
phoenix/db/types/token_price_customization.py,sha256=LAb8IwyFJGDCje7CvcPcxNp4NOU8si5hSYaf1fyi624,857
|
|
59
|
-
phoenix/db/types/trace_retention.py,sha256=
|
|
59
|
+
phoenix/db/types/trace_retention.py,sha256=Y97xfqNa4AO2j6RhhUJIbO6duQ1oX7Vu5bC9laydf1g,9899
|
|
60
60
|
phoenix/experiments/__init__.py,sha256=6JGwgUd7xCbGpuHqYZlsmErmYvVgv7N_j43bn3dUqsk,123
|
|
61
61
|
phoenix/experiments/functions.py,sha256=J9Bfp7ptDB2g6DpSqgf2iw4cDL09P_h4UWAAtJNS9Mg,39971
|
|
62
62
|
phoenix/experiments/tracing.py,sha256=X-wlgbWEzP1oHkLmjop3fGjONo6JK5a0kPXc9YYD014,2856
|
|
@@ -124,7 +124,7 @@ phoenix/server/api/dataloaders/average_experiment_run_latency.py,sha256=_wEcC47z
|
|
|
124
124
|
phoenix/server/api/dataloaders/dataset_example_revisions.py,sha256=xF7M2dg3UmjhdCrscnztCIBBI0cg3RF48IIqvilpc18,4623
|
|
125
125
|
phoenix/server/api/dataloaders/dataset_example_spans.py,sha256=z_MFquqAcJ9wat7BBp7MVeJ9BYuu2EZEdaog52iWDno,1390
|
|
126
126
|
phoenix/server/api/dataloaders/document_evaluation_summaries.py,sha256=9fdROnzp-mymggHwNvpRkCk93LUFxxLy55-j3HP_2HY,5565
|
|
127
|
-
phoenix/server/api/dataloaders/document_evaluations.py,sha256=
|
|
127
|
+
phoenix/server/api/dataloaders/document_evaluations.py,sha256=1bpBDydd-9YvhgkNoE5w-FGrjtnjUztcnjZTdMAazws,1072
|
|
128
128
|
phoenix/server/api/dataloaders/document_retrieval_metrics.py,sha256=37EcAW7oYQuWYHMDHb0wcqbWj9lhSskvzDO7NJbT5Js,4136
|
|
129
129
|
phoenix/server/api/dataloaders/experiment_annotation_summaries.py,sha256=CFVj7DwFYj330FLU5w3zEr12AGUX1e8ZX0X5buxMuEk,5643
|
|
130
130
|
phoenix/server/api/dataloaders/experiment_error_rates.py,sha256=06IZF07qt2y167DBM49QkSNdnphPArhcsgYFcunaL-U,1992
|
|
@@ -257,9 +257,9 @@ phoenix/server/api/routers/oauth2.py,sha256=rPcKFvfijzBYLjfwbCNzCn0ihn4wGWh4xh6B
|
|
|
257
257
|
phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
|
|
258
258
|
phoenix/server/api/routers/v1/__init__.py,sha256=_CxVCs26dPuC2KygV3VzYqmoAECeFwiSTava9mxIJTE,2790
|
|
259
259
|
phoenix/server/api/routers/v1/annotation_configs.py,sha256=xp5lJmKYlRsINCUrRD9-lTAElw2v4hdFndS5BWrxICA,16048
|
|
260
|
-
phoenix/server/api/routers/v1/annotations.py,sha256=
|
|
260
|
+
phoenix/server/api/routers/v1/annotations.py,sha256=V_Cm_XOJaSVpMsTCfxBd1H39sk17b2y6Lyk5qwwDHvI,11433
|
|
261
261
|
phoenix/server/api/routers/v1/datasets.py,sha256=9iPORLmbOrPKgUUcRDMs6ZczSIz7hvc6bngJy3IbdR0,38331
|
|
262
|
-
phoenix/server/api/routers/v1/documents.py,sha256=
|
|
262
|
+
phoenix/server/api/routers/v1/documents.py,sha256=iA_vYU6_p2-pazh_Rp930kiOsiHYFIPPqsZSSXwPgVI,5733
|
|
263
263
|
phoenix/server/api/routers/v1/evaluations.py,sha256=aBrPO-xCAWyTxydaHq7W2wQFm65k89uVR-H3VWsd6WQ,13062
|
|
264
264
|
phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=DZ3UK9OoYKElpRcEER7559-KiAqWr-1IXpZ27FbfP3k,5249
|
|
265
265
|
phoenix/server/api/routers/v1/experiment_runs.py,sha256=LZeCQWQIEOZ9jK5Gp_C4JbiYY6AmnnWe85cVcvdkCLE,7107
|
|
@@ -267,8 +267,8 @@ phoenix/server/api/routers/v1/experiments.py,sha256=hIBecGACzGZEgl93ap_JV52pUv-I
|
|
|
267
267
|
phoenix/server/api/routers/v1/models.py,sha256=p3gJN-9SWiUYTUTft4bZMsZVCBNTb4nN1Foy68eRZzQ,1997
|
|
268
268
|
phoenix/server/api/routers/v1/projects.py,sha256=XR6uJxHXXtC1q8LNyS9W6iaj440sv1OKCu-OSBfxEys,12824
|
|
269
269
|
phoenix/server/api/routers/v1/prompts.py,sha256=chRYcLkOYDJdJfVZVukVTUyIRnLPvsJCg41CuPxOIU8,26695
|
|
270
|
-
phoenix/server/api/routers/v1/spans.py,sha256=
|
|
271
|
-
phoenix/server/api/routers/v1/traces.py,sha256
|
|
270
|
+
phoenix/server/api/routers/v1/spans.py,sha256=9xWj5RZEGCjOUVCx9bl-tHdQrIS6VvaUROfo3ROg7J4,47583
|
|
271
|
+
phoenix/server/api/routers/v1/traces.py,sha256=-_g-Imy31J_jWyCOMIOAHMEdMB6KxeLRNW56r9-6REI,9849
|
|
272
272
|
phoenix/server/api/routers/v1/users.py,sha256=eO8zMtGU33Td2_G1l9D7Z0a4CG1CwBUCj_Z9z2uk7wg,12089
|
|
273
273
|
phoenix/server/api/routers/v1/utils.py,sha256=oXIOGPzPTkE0ZWUTRCoRIQQ7wTzoSwtWFaUSjlGBqts,4960
|
|
274
274
|
phoenix/server/api/types/Annotation.py,sha256=gsl8CwjIbDUbZRj4d9USwZ_w_Tkz4i7zuZh9ftV80jA,1132
|
|
@@ -285,9 +285,10 @@ phoenix/server/api/types/CostBreakdown.py,sha256=yw9dlb0blGIB_dWNP8yEvDHJztHjpiV
|
|
|
285
285
|
phoenix/server/api/types/CreateDatasetPayload.py,sha256=R-6zCmuD0f76RU9Giu78xwTHlASQs6Aq8yzvX1Kxc3g,140
|
|
286
286
|
phoenix/server/api/types/CronExpression.py,sha256=R7oxuSSX_eTUHQWaoaSueQqWDmkkHr5dBKRN6q-6ROk,331
|
|
287
287
|
phoenix/server/api/types/DataQualityMetric.py,sha256=Aieg3bHeBFaAf4mqeRcH1zT04sXAtQD8ATSHJt7FaBQ,1538
|
|
288
|
-
phoenix/server/api/types/Dataset.py,sha256=
|
|
288
|
+
phoenix/server/api/types/Dataset.py,sha256=OvCgbqqNF-bj0iaQldIeEfZiMjyXGFsozIIBsbJJwq4,12648
|
|
289
289
|
phoenix/server/api/types/DatasetExample.py,sha256=_9byxGpXfYb-hmFMUJeG7Bw1wsRKSJaHwF2IPAbFpFw,3115
|
|
290
290
|
phoenix/server/api/types/DatasetExampleRevision.py,sha256=c-jWR6dTguEZTm54IMlFr0Ic84I3nefyDnZb7nF5hnI,874
|
|
291
|
+
phoenix/server/api/types/DatasetExperimentAnnotationSummary.py,sha256=EVXz6zfbdYsU5SZ6FUrAA_VQqQCnBE_mbKgmAmFoDLI,195
|
|
291
292
|
phoenix/server/api/types/DatasetValues.py,sha256=7VbCOLlzOXpZN80-zYF2UGuafRcPsZF-8WQNc0YsKFc,1119
|
|
292
293
|
phoenix/server/api/types/DatasetVersion.py,sha256=NnDriqQUE20N_Jhyu3-IxHWJQE9wmdHEJIQAKyTVJpo,302
|
|
293
294
|
phoenix/server/api/types/Dimension.py,sha256=VjW6kOMhFslGSSHlsylpF1hYY3wuhopC2LM2eOcIhRI,10665
|
|
@@ -295,11 +296,11 @@ phoenix/server/api/types/DimensionDataType.py,sha256=o0QQRpUzgXbhd10drR2LthxrjB8
|
|
|
295
296
|
phoenix/server/api/types/DimensionShape.py,sha256=LNsRt3Uyx1OmwMa5EXk1JGRXPLVV2EEgYXz-6wnLJn4,562
|
|
296
297
|
phoenix/server/api/types/DimensionType.py,sha256=JLikZUBVqbHlUWcYYd8d60gB1_hmAcuFuZZsjCXpIwc,801
|
|
297
298
|
phoenix/server/api/types/DimensionWithValue.py,sha256=4_koirnDrZBBdFkIWka2f-OeqZ6IzQXwqz3ccjdgHrc,484
|
|
299
|
+
phoenix/server/api/types/DocumentAnnotation.py,sha256=CMNcODXnKYzAUBITp_iYOoCGpXPEFGnelxmWySqDyn4,3076
|
|
298
300
|
phoenix/server/api/types/DocumentEvaluationSummary.py,sha256=dx4Btlfw9_XsfmibjfWvMOvUfrvr9oGJq8jYmIL2o-Q,3494
|
|
299
301
|
phoenix/server/api/types/DocumentRetrievalMetrics.py,sha256=amkpC3H5IU5-9GvO0telpbq00m6lIcv_2v446OpwFwc,1822
|
|
300
302
|
phoenix/server/api/types/EmbeddingDimension.py,sha256=AYvpZ1nWINAgN4BAZsA_xI_2TNFK6h5jmqzvkPs651M,19428
|
|
301
303
|
phoenix/server/api/types/EmbeddingMetadata.py,sha256=fJvNNYCbkf3SJalArLy9rcBq9Uj1SNac60zjqe1PFnM,461
|
|
302
|
-
phoenix/server/api/types/Evaluation.py,sha256=qkMmq5G9pgwUNOPSTRwR-nNAoqLT3LlXrq7O8cAgXQ8,1356
|
|
303
304
|
phoenix/server/api/types/EvaluationSummary.py,sha256=vILYejnfPvMwWEXOwhQZsANvYe3AdO2OkMR2rcgp1H4,1512
|
|
304
305
|
phoenix/server/api/types/Event.py,sha256=iYt_Jx1Roioo0vZ0iPeJTHcTu6NSm4ilVMJ-IMUHAKk,3970
|
|
305
306
|
phoenix/server/api/types/EventMetadata.py,sha256=-J0tYF9eZTHwCjwxQHY7Gckr2_MNW5OoWT1mydweZNM,635
|
|
@@ -338,7 +339,7 @@ phoenix/server/api/types/ScalarDriftMetricEnum.py,sha256=IUAcRPpgL41WdoIgK6cNk2T
|
|
|
338
339
|
phoenix/server/api/types/Segments.py,sha256=vT2v0efoa5cuBKxLtxTnsUP5YJJCZfTloM71Spu0tMI,2915
|
|
339
340
|
phoenix/server/api/types/ServerStatus.py,sha256=t92OHuVhK9DXDk2vsBuHceQNKqYGpHwUp8DNGKz2wOk,88
|
|
340
341
|
phoenix/server/api/types/SortDir.py,sha256=OUpXhlCzCxPoXSDkJJygEs9Rw9pMymfaZUG5zPTrw4Y,152
|
|
341
|
-
phoenix/server/api/types/Span.py,sha256=
|
|
342
|
+
phoenix/server/api/types/Span.py,sha256=wJEEReo38Qsc0nFuBF0LDq9kcfGwmYerA86FXC4GqzE,32130
|
|
342
343
|
phoenix/server/api/types/SpanAnnotation.py,sha256=uPWu7Z8rmpfKhaaxbged4_o00pPCR3nkn7Gji9vB8jY,1959
|
|
343
344
|
phoenix/server/api/types/SpanCostDetailSummaryEntry.py,sha256=RXAdOC6MFyR9mwaoj8lMMdI3_9r3z6mR2izJvlsj12U,252
|
|
344
345
|
phoenix/server/api/types/SpanCostSummary.py,sha256=wo03FCMcFzB5m4P5kvA5jzi9ACLbht38ozQbDJUh94g,357
|
|
@@ -350,7 +351,7 @@ phoenix/server/api/types/TokenPrice.py,sha256=OyCZgXCKDIihmDalHvkUDvhvl2Vsxx1J2U
|
|
|
350
351
|
phoenix/server/api/types/TokenUsage.py,sha256=aeMPwEDqu8MWT2Jc5sq6Sfb_7kcR_KTzZi6UB9OK7Yg,207
|
|
351
352
|
phoenix/server/api/types/ToolDefinition.py,sha256=T6UH2vcbuPBDy7jKYOqMth2NdqxMPgDBf11Tpbt5Yb8,187
|
|
352
353
|
phoenix/server/api/types/Trace.py,sha256=iaSAicxMHTrnUTYxNfMwFMglVeptqu7nF_AHS6k-QGw,9718
|
|
353
|
-
phoenix/server/api/types/TraceAnnotation.py,sha256=
|
|
354
|
+
phoenix/server/api/types/TraceAnnotation.py,sha256=NAZ5HXH31x6zjXlh-1CUiXjbiCo0iF6hqIrgts2h3Lo,2022
|
|
354
355
|
phoenix/server/api/types/UMAPPoints.py,sha256=49sWnxjcAJKRzqUY71Fa0tOPti5XjIIFT5cSg6oNu_U,1650
|
|
355
356
|
phoenix/server/api/types/User.py,sha256=20q4K_dwWHKdCTLFOmpdbt_OjdGv-98z2dHhwh7RmnI,2412
|
|
356
357
|
phoenix/server/api/types/UserApiKey.py,sha256=EcsPNxV_PtgpqwA_bPTj_aRYt15l6X24YDy20hUPywU,1251
|
|
@@ -364,7 +365,7 @@ phoenix/server/cost_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
364
365
|
phoenix/server/cost_tracking/cost_details_calculator.py,sha256=Tt0YcuLhgPuXKWJemWVmYQfG0xQUvH4VziIj6KcDnoA,8945
|
|
365
366
|
phoenix/server/cost_tracking/cost_model_lookup.py,sha256=jhtVdnQBzrTUHeOGPWgOebk-Io5hpJ1vAgWOu8ojeJ4,6801
|
|
366
367
|
phoenix/server/cost_tracking/helpers.py,sha256=Pk6ECjnYreTxrldtRwxnwFcxIPVsvDq_yAwDA_spkOc,2122
|
|
367
|
-
phoenix/server/cost_tracking/model_cost_manifest.json,sha256=
|
|
368
|
+
phoenix/server/cost_tracking/model_cost_manifest.json,sha256=hOe6WLOd4t99iMCwCVbOya9kERKb7Ci_NR2opiNQY5M,65570
|
|
368
369
|
phoenix/server/cost_tracking/regex_specificity.py,sha256=9kqWuQ68C-hlwW25hr7BhFlRt5y2Nnpy0Ax3n9UN6Xk,11622
|
|
369
370
|
phoenix/server/cost_tracking/token_cost_calculator.py,sha256=2JEZnvusx2-xbhp8krp9EarjWuyGH2KO4e-ZwJX-K0s,1598
|
|
370
371
|
phoenix/server/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -393,10 +394,10 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
393
394
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
394
395
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
395
396
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
396
|
-
phoenix/server/static/.vite/manifest.json,sha256
|
|
397
|
-
phoenix/server/static/assets/components-
|
|
398
|
-
phoenix/server/static/assets/index-
|
|
399
|
-
phoenix/server/static/assets/pages-
|
|
397
|
+
phoenix/server/static/.vite/manifest.json,sha256=-oRiGNtI_bi_1cZQ95g-a5uptY0yHoFkRH9om8VpChY,2328
|
|
398
|
+
phoenix/server/static/assets/components-BjW5gAwL.js,sha256=VHX557qhyBcOfSYPZtR1TtOW1AMeAaJiqvYZ1cDaOf4,664533
|
|
399
|
+
phoenix/server/static/assets/index-3OI8VV_W.js,sha256=K7HPjzp6bjscIw5R4KHQS32wo6oJhcrRTA7myP9dKlM,63396
|
|
400
|
+
phoenix/server/static/assets/pages-CQfUODtD.js,sha256=7iODh8TLYLYZGeEAl3dW9jJcCyP1EkKS_OluUdNVMpc,1269654
|
|
400
401
|
phoenix/server/static/assets/vendor-CqDb5u4o.css,sha256=zIyFiNJKxMaQk8AvtLgt1rR01oO10d1MFndSDKH9Clw,5517
|
|
401
402
|
phoenix/server/static/assets/vendor-RdRDaQiR.js,sha256=oTxLetZZXJ20yoKNAYExto9V73y8X5zjddWV46K9CWM,2595492
|
|
402
403
|
phoenix/server/static/assets/vendor-arizeai-DsYDNOqt.js,sha256=0HIkPJXbKTh85nqphdAXYeStRzdaim0IQxRXiXxa21U,121514
|
|
@@ -418,7 +419,7 @@ phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKytt
|
|
|
418
419
|
phoenix/trace/exporter.py,sha256=bUXh8fjJIbHurrnt4bAm-cCWqUN5FqNsIc8DZzzklkQ,4695
|
|
419
420
|
phoenix/trace/fixtures.py,sha256=1c7fsyvmxC53Fib9T_Qxp_Ly3OZdDbkLQ0XpFzikEjk,20298
|
|
420
421
|
phoenix/trace/otel.py,sha256=RJSbAuzS4KBS0t-fntXQaaYwv7FmIXRMrw65DI67z8k,10622
|
|
421
|
-
phoenix/trace/projects.py,sha256=
|
|
422
|
+
phoenix/trace/projects.py,sha256=2wVg_UaNiXuhn60qJhgqJiaQ0OnbOWMnB10QeTGZGwQ,2420
|
|
422
423
|
phoenix/trace/schemas.py,sha256=Su6e567Bei9oo6PsWO2srTcPAj9C2bMgbGtx64Sgqeg,6332
|
|
423
424
|
phoenix/trace/span_evaluations.py,sha256=x3nye9r2SQk1mrR3N05YbuWsgUKpMWwTRBtJTDBSj3Y,13156
|
|
424
425
|
phoenix/trace/span_json_decoder.py,sha256=J1_oDViuUoC4vxPg61U4EOZC1uEMcCzoj-kVjOFEE8k,3224
|
|
@@ -443,9 +444,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
|
|
|
443
444
|
phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
|
|
444
445
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
445
446
|
phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
|
|
446
|
-
arize_phoenix-11.
|
|
447
|
-
arize_phoenix-11.
|
|
448
|
-
arize_phoenix-11.
|
|
449
|
-
arize_phoenix-11.
|
|
450
|
-
arize_phoenix-11.
|
|
451
|
-
arize_phoenix-11.
|
|
447
|
+
arize_phoenix-11.31.0.dist-info/METADATA,sha256=E2fweFHv4hduXa1oIQzqu4LX29_zspyxGa8bdGq13zs,32877
|
|
448
|
+
arize_phoenix-11.31.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
449
|
+
arize_phoenix-11.31.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
|
|
450
|
+
arize_phoenix-11.31.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
451
|
+
arize_phoenix-11.31.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
452
|
+
arize_phoenix-11.31.0.dist-info/RECORD,,
|
|
@@ -199,7 +199,7 @@ class TraceRetentionCronExpression(RootModel[str]):
|
|
|
199
199
|
|
|
200
200
|
def _parse_field(field: str, min_val: int, max_val: int) -> set[int]:
|
|
201
201
|
"""
|
|
202
|
-
|
|
202
|
+
Parses a cron field and returns the set of matching values.
|
|
203
203
|
|
|
204
204
|
Args:
|
|
205
205
|
field (str): The cron field to parse
|
|
@@ -5,11 +5,10 @@ from strawberry.dataloader import DataLoader
|
|
|
5
5
|
from typing_extensions import TypeAlias
|
|
6
6
|
|
|
7
7
|
from phoenix.db import models
|
|
8
|
-
from phoenix.server.api.types.Evaluation import DocumentAnnotation
|
|
9
8
|
from phoenix.server.types import DbSessionFactory
|
|
10
9
|
|
|
11
10
|
Key: TypeAlias = int
|
|
12
|
-
Result: TypeAlias = list[DocumentAnnotation]
|
|
11
|
+
Result: TypeAlias = list[models.DocumentAnnotation]
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class DocumentEvaluationsDataLoader(DataLoader[Key, Result]):
|
|
@@ -18,14 +17,12 @@ class DocumentEvaluationsDataLoader(DataLoader[Key, Result]):
|
|
|
18
17
|
self._db = db
|
|
19
18
|
|
|
20
19
|
async def _load_fn(self, keys: list[Key]) -> list[Result]:
|
|
21
|
-
|
|
20
|
+
document_annotations_by_id: defaultdict[Key, Result] = defaultdict(list)
|
|
22
21
|
mda = models.DocumentAnnotation
|
|
23
22
|
async with self._db() as session:
|
|
24
|
-
data = await session.stream_scalars(
|
|
25
|
-
select(mda).where(mda.span_rowid.in_(keys)).where(mda.annotator_kind == "LLM")
|
|
26
|
-
)
|
|
23
|
+
data = await session.stream_scalars(select(mda).where(mda.span_rowid.in_(keys)))
|
|
27
24
|
async for document_evaluation in data:
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
document_annotations_by_id[document_evaluation.span_rowid].append(
|
|
26
|
+
document_evaluation
|
|
30
27
|
)
|
|
31
|
-
return [
|
|
28
|
+
return [document_annotations_by_id[key] for key in keys]
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from typing import Literal, Optional
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from typing import Any, Literal, Optional
|
|
6
6
|
|
|
7
7
|
from fastapi import APIRouter, HTTPException, Path, Query
|
|
8
|
+
from pydantic import Field
|
|
8
9
|
from sqlalchemy import exists, select
|
|
9
10
|
from starlette.requests import Request
|
|
10
11
|
from starlette.status import HTTP_200_OK, HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY
|
|
11
12
|
from strawberry.relay import GlobalID
|
|
12
13
|
|
|
13
14
|
from phoenix.db import models
|
|
15
|
+
from phoenix.db.insertion.types import Precursors
|
|
16
|
+
from phoenix.server.api.routers.v1.models import V1RoutesBaseModel
|
|
14
17
|
from phoenix.server.api.types.SpanAnnotation import SpanAnnotation as SpanAnnotationNodeType
|
|
18
|
+
from phoenix.server.api.types.TraceAnnotation import TraceAnnotation as TraceAnnotationNodeType
|
|
15
19
|
from phoenix.server.api.types.User import User as UserNodeType
|
|
16
20
|
|
|
17
|
-
from .spans import SpanAnnotationData, SpanAnnotationResult
|
|
18
21
|
from .utils import PaginatedResponseBody, _get_project_by_identifier, add_errors_to_responses
|
|
19
22
|
|
|
20
23
|
logger = logging.getLogger(__name__)
|
|
21
24
|
|
|
22
25
|
SPAN_ANNOTATION_NODE_NAME = SpanAnnotationNodeType.__name__
|
|
26
|
+
TRACE_ANNOTATION_NODE_NAME = TraceAnnotationNodeType.__name__
|
|
27
|
+
MAX_TRACE_IDS = 1_000
|
|
23
28
|
USER_NODE_NAME = UserNodeType.__name__
|
|
24
29
|
MAX_SPAN_IDS = 1_000
|
|
30
|
+
MAX_SESSION_IDS = 1_000
|
|
25
31
|
|
|
26
32
|
router = APIRouter(tags=["annotations"])
|
|
27
33
|
|
|
28
34
|
|
|
29
|
-
class
|
|
35
|
+
class Annotation(V1RoutesBaseModel):
|
|
30
36
|
id: str
|
|
31
37
|
created_at: datetime
|
|
32
38
|
updated_at: datetime
|
|
@@ -34,10 +40,127 @@ class SpanAnnotation(SpanAnnotationData):
|
|
|
34
40
|
user_id: Optional[str]
|
|
35
41
|
|
|
36
42
|
|
|
43
|
+
class AnnotationResult(V1RoutesBaseModel):
|
|
44
|
+
label: Optional[str] = Field(default=None, description="The label assigned by the annotation")
|
|
45
|
+
score: Optional[float] = Field(default=None, description="The score assigned by the annotation")
|
|
46
|
+
explanation: Optional[str] = Field(
|
|
47
|
+
default=None, description="Explanation of the annotation result"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AnnotationData(V1RoutesBaseModel):
|
|
52
|
+
name: str = Field(description="The name of the annotation")
|
|
53
|
+
annotator_kind: Literal["LLM", "CODE", "HUMAN"] = Field(
|
|
54
|
+
description="The kind of annotator used for the annotation"
|
|
55
|
+
)
|
|
56
|
+
result: Optional[AnnotationResult] = Field(
|
|
57
|
+
default=None, description="The result of the annotation"
|
|
58
|
+
)
|
|
59
|
+
metadata: Optional[dict[str, Any]] = Field(
|
|
60
|
+
default=None, description="Metadata for the annotation"
|
|
61
|
+
)
|
|
62
|
+
identifier: str = Field(
|
|
63
|
+
default="",
|
|
64
|
+
description=(
|
|
65
|
+
"The identifier of the annotation. "
|
|
66
|
+
"If provided, the annotation will be updated if it already exists."
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class SpanAnnotationData(AnnotationData):
|
|
72
|
+
span_id: str = Field(description="OpenTelemetry Span ID (hex format w/o 0x prefix)")
|
|
73
|
+
|
|
74
|
+
def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.SpanAnnotation:
|
|
75
|
+
return Precursors.SpanAnnotation(
|
|
76
|
+
datetime.now(timezone.utc),
|
|
77
|
+
self.span_id,
|
|
78
|
+
models.SpanAnnotation(
|
|
79
|
+
name=self.name,
|
|
80
|
+
annotator_kind=self.annotator_kind,
|
|
81
|
+
score=self.result.score if self.result else None,
|
|
82
|
+
label=self.result.label if self.result else None,
|
|
83
|
+
explanation=self.result.explanation if self.result else None,
|
|
84
|
+
metadata_=self.metadata or {},
|
|
85
|
+
identifier=self.identifier,
|
|
86
|
+
source="API",
|
|
87
|
+
user_id=user_id,
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class SpanAnnotation(SpanAnnotationData, Annotation):
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
37
96
|
class SpanAnnotationsResponseBody(PaginatedResponseBody[SpanAnnotation]):
|
|
38
97
|
pass
|
|
39
98
|
|
|
40
99
|
|
|
100
|
+
class SpanDocumentAnnotationData(AnnotationData):
|
|
101
|
+
span_id: str = Field(description="OpenTelemetry Span ID (hex format w/o 0x prefix)")
|
|
102
|
+
document_position: int = Field(
|
|
103
|
+
description="A 0 based index of the document. E.x. the first document during retrieval is 0"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Precursor here means a value to add to a queue for processing async
|
|
107
|
+
def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.DocumentAnnotation:
|
|
108
|
+
return Precursors.DocumentAnnotation(
|
|
109
|
+
datetime.now(timezone.utc),
|
|
110
|
+
self.span_id,
|
|
111
|
+
self.document_position,
|
|
112
|
+
models.DocumentAnnotation(
|
|
113
|
+
name=self.name,
|
|
114
|
+
annotator_kind=self.annotator_kind,
|
|
115
|
+
document_position=self.document_position,
|
|
116
|
+
score=self.result.score if self.result else None,
|
|
117
|
+
label=self.result.label if self.result else None,
|
|
118
|
+
explanation=self.result.explanation if self.result else None,
|
|
119
|
+
metadata_=self.metadata or {},
|
|
120
|
+
identifier=self.identifier,
|
|
121
|
+
source="API",
|
|
122
|
+
user_id=user_id,
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class SpanDocumentAnnotation(SpanDocumentAnnotationData, Annotation):
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SpanDocumentAnnotationsResponseBody(PaginatedResponseBody[SpanDocumentAnnotation]):
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class TraceAnnotationData(AnnotationData):
|
|
136
|
+
trace_id: str = Field(description="OpenTelemetry Trace ID (hex format w/o 0x prefix)")
|
|
137
|
+
|
|
138
|
+
def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.TraceAnnotation:
|
|
139
|
+
return Precursors.TraceAnnotation(
|
|
140
|
+
datetime.now(timezone.utc),
|
|
141
|
+
self.trace_id,
|
|
142
|
+
models.TraceAnnotation(
|
|
143
|
+
name=self.name,
|
|
144
|
+
annotator_kind=self.annotator_kind,
|
|
145
|
+
score=self.result.score if self.result else None,
|
|
146
|
+
label=self.result.label if self.result else None,
|
|
147
|
+
explanation=self.result.explanation if self.result else None,
|
|
148
|
+
metadata_=self.metadata or {},
|
|
149
|
+
identifier=self.identifier,
|
|
150
|
+
source="API",
|
|
151
|
+
user_id=user_id,
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class TraceAnnotation(TraceAnnotationData, Annotation):
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class TraceAnnotationsResponseBody(PaginatedResponseBody[TraceAnnotation]):
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
|
|
41
164
|
@router.get(
|
|
42
165
|
"/projects/{project_identifier}/span_annotations",
|
|
43
166
|
operation_id="listSpanAnnotationsBySpanIds",
|
|
@@ -164,7 +287,7 @@ async def list_span_annotations(
|
|
|
164
287
|
id=str(GlobalID(SPAN_ANNOTATION_NODE_NAME, str(anno.id))),
|
|
165
288
|
span_id=span_id,
|
|
166
289
|
name=anno.name,
|
|
167
|
-
result=
|
|
290
|
+
result=AnnotationResult(
|
|
168
291
|
label=anno.label,
|
|
169
292
|
score=anno.score,
|
|
170
293
|
explanation=anno.explanation,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import Any, Literal, Optional
|
|
1
|
+
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
5
4
|
from pydantic import Field
|
|
@@ -11,64 +10,19 @@ from strawberry.relay import GlobalID
|
|
|
11
10
|
from phoenix.db import models
|
|
12
11
|
from phoenix.db.helpers import SupportedSQLDialect
|
|
13
12
|
from phoenix.db.insertion.helpers import as_kv, insert_on_conflict
|
|
14
|
-
from phoenix.
|
|
15
|
-
from phoenix.server.api.types.
|
|
13
|
+
from phoenix.server.api.routers.v1.annotations import SpanDocumentAnnotationData
|
|
14
|
+
from phoenix.server.api.types.DocumentAnnotation import DocumentAnnotation
|
|
16
15
|
from phoenix.server.authorization import is_not_locked
|
|
17
16
|
from phoenix.server.bearer_auth import PhoenixUser
|
|
18
17
|
from phoenix.server.dml_event import DocumentAnnotationInsertEvent
|
|
19
18
|
|
|
20
19
|
from .models import V1RoutesBaseModel
|
|
21
|
-
from .spans import SpanAnnotationResult
|
|
22
20
|
from .utils import RequestBody, ResponseBody, add_errors_to_responses
|
|
23
21
|
|
|
24
22
|
# Since the document annotations are spans related, we place it under spans
|
|
25
23
|
router = APIRouter(tags=["spans"])
|
|
26
24
|
|
|
27
25
|
|
|
28
|
-
class SpanDocumentAnnotationData(V1RoutesBaseModel):
|
|
29
|
-
span_id: str = Field(description="OpenTelemetry Span ID (hex format w/o 0x prefix)")
|
|
30
|
-
name: str = Field(description="The name of the document annotation. E.x. relevance")
|
|
31
|
-
annotator_kind: Literal["LLM", "CODE", "HUMAN"] = Field(
|
|
32
|
-
description="The kind of annotator. E.g. llm judge, a heuristic piece of code, or a human"
|
|
33
|
-
)
|
|
34
|
-
document_position: int = Field(
|
|
35
|
-
description="A 0 based index of the document. E.x. the first document during retrieval is 0"
|
|
36
|
-
)
|
|
37
|
-
result: Optional[SpanAnnotationResult] = Field(
|
|
38
|
-
default=None, description="The score and or label of the annotation"
|
|
39
|
-
)
|
|
40
|
-
metadata: Optional[dict[str, Any]] = Field(
|
|
41
|
-
default=None, description="Metadata for custom values of the annotation"
|
|
42
|
-
)
|
|
43
|
-
identifier: str = Field(
|
|
44
|
-
default="",
|
|
45
|
-
description=(
|
|
46
|
-
"An custom ID for the annotation. "
|
|
47
|
-
"If provided, the annotation will be updated if it already exists."
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
# Precursor here means a value to add to a queue for processing async
|
|
52
|
-
def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.DocumentAnnotation:
|
|
53
|
-
return Precursors.DocumentAnnotation(
|
|
54
|
-
datetime.now(timezone.utc),
|
|
55
|
-
self.span_id,
|
|
56
|
-
self.document_position,
|
|
57
|
-
models.DocumentAnnotation(
|
|
58
|
-
name=self.name,
|
|
59
|
-
annotator_kind=self.annotator_kind,
|
|
60
|
-
document_position=self.document_position,
|
|
61
|
-
score=self.result.score if self.result else None,
|
|
62
|
-
label=self.result.label if self.result else None,
|
|
63
|
-
explanation=self.result.explanation if self.result else None,
|
|
64
|
-
metadata_=self.metadata or {},
|
|
65
|
-
identifier=self.identifier,
|
|
66
|
-
source="API",
|
|
67
|
-
user_id=user_id,
|
|
68
|
-
),
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
26
|
class AnnotateSpanDocumentsRequestBody(RequestBody[list[SpanDocumentAnnotationData]]):
|
|
73
27
|
pass
|
|
74
28
|
|
|
@@ -90,7 +44,11 @@ class AnnotateSpanDocumentsResponseBody(ResponseBody[list[InsertedSpanDocumentAn
|
|
|
90
44
|
{
|
|
91
45
|
"status_code": HTTP_404_NOT_FOUND,
|
|
92
46
|
"description": "Span not found",
|
|
93
|
-
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"status_code": 422,
|
|
50
|
+
"description": "Invalid request - non-empty identifier not supported",
|
|
51
|
+
},
|
|
94
52
|
]
|
|
95
53
|
),
|
|
96
54
|
response_description="Span document annotation inserted successfully",
|
|
@@ -106,6 +64,14 @@ async def annotate_span_documents(
|
|
|
106
64
|
if not request_body.data:
|
|
107
65
|
return AnnotateSpanDocumentsResponseBody(data=[])
|
|
108
66
|
|
|
67
|
+
# Validate that identifiers are empty or only whitespace
|
|
68
|
+
for annotation in request_body.data:
|
|
69
|
+
if annotation.identifier.strip():
|
|
70
|
+
raise HTTPException(
|
|
71
|
+
detail=f"Non-empty identifier '{annotation.identifier}' is not supported",
|
|
72
|
+
status_code=422, # Unprocessable Entity
|
|
73
|
+
)
|
|
74
|
+
|
|
109
75
|
user_id: Optional[int] = None
|
|
110
76
|
if request.app.state.authentication_enabled and isinstance(request.user, PhoenixUser):
|
|
111
77
|
user_id = int(request.user.identity)
|
|
@@ -117,6 +83,7 @@ async def annotate_span_documents(
|
|
|
117
83
|
]
|
|
118
84
|
if not sync:
|
|
119
85
|
await request.state.enqueue(*precursors)
|
|
86
|
+
return AnnotateSpanDocumentsResponseBody(data=[])
|
|
120
87
|
|
|
121
88
|
span_ids = {p.span_id for p in precursors}
|
|
122
89
|
# Account for the fact that the spans could arrive after the annotation
|
|
@@ -130,38 +97,39 @@ async def annotate_span_documents(
|
|
|
130
97
|
)
|
|
131
98
|
}
|
|
132
99
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
raise HTTPException(
|
|
137
|
-
detail=f"Spans with IDs {', '.join(missing_span_ids)} do not exist.",
|
|
138
|
-
status_code=HTTP_404_NOT_FOUND,
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
# Validate that document positions are within bounds
|
|
142
|
-
for annotation in span_document_annotations:
|
|
143
|
-
_, num_docs = existing_spans[annotation.span_id]
|
|
144
|
-
if annotation.document_position not in range(num_docs):
|
|
100
|
+
missing_span_ids = span_ids - set(existing_spans.keys())
|
|
101
|
+
# We prefer to fail the entire operation if there are missing spans in sync mode
|
|
102
|
+
if missing_span_ids:
|
|
145
103
|
raise HTTPException(
|
|
146
|
-
detail=f"
|
|
147
|
-
|
|
148
|
-
status_code=422, # Unprocessable Entity
|
|
104
|
+
detail=f"Spans with IDs {', '.join(missing_span_ids)} do not exist.",
|
|
105
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
149
106
|
)
|
|
150
107
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
108
|
+
# Validate that document positions are within bounds
|
|
109
|
+
for annotation in span_document_annotations:
|
|
110
|
+
_, num_docs = existing_spans[annotation.span_id]
|
|
111
|
+
if annotation.document_position not in range(num_docs):
|
|
112
|
+
raise HTTPException(
|
|
113
|
+
detail=f"Document position {annotation.document_position} is out of bounds for "
|
|
114
|
+
f"span {annotation.span_id} (max: {num_docs - 1})",
|
|
115
|
+
status_code=422, # Unprocessable Entity
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
inserted_document_annotation_ids = []
|
|
119
|
+
dialect = SupportedSQLDialect(session.bind.dialect.name)
|
|
120
|
+
for anno in precursors:
|
|
121
|
+
span_rowid, _ = existing_spans[anno.span_id]
|
|
122
|
+
values = dict(as_kv(anno.as_insertable(span_rowid).row))
|
|
123
|
+
span_document_annotation_id = await session.scalar(
|
|
124
|
+
insert_on_conflict(
|
|
125
|
+
values,
|
|
126
|
+
dialect=dialect,
|
|
127
|
+
table=models.DocumentAnnotation,
|
|
128
|
+
unique_by=("name", "span_rowid", "identifier", "document_position"),
|
|
129
|
+
constraint_name="uq_document_annotations_name_span_rowid_document_pos_identifier",
|
|
130
|
+
).returning(models.DocumentAnnotation.id)
|
|
131
|
+
)
|
|
132
|
+
inserted_document_annotation_ids.append(span_document_annotation_id)
|
|
165
133
|
|
|
166
134
|
# We queue an event to let the application know that annotations have changed
|
|
167
135
|
request.state.event_queue.put(
|