arize-phoenix 11.10.0__py3-none-any.whl → 11.11.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.10.0.dist-info → arize_phoenix-11.11.0.dist-info}/METADATA +1 -1
- {arize_phoenix-11.10.0.dist-info → arize_phoenix-11.11.0.dist-info}/RECORD +22 -21
- phoenix/server/api/helpers/playground_clients.py +146 -63
- phoenix/server/api/helpers/playground_registry.py +2 -2
- phoenix/server/api/input_types/PromptFilter.py +14 -0
- phoenix/server/api/queries.py +14 -3
- phoenix/server/api/types/GenerativeModel.py +48 -3
- phoenix/server/api/types/Project.py +125 -0
- phoenix/server/static/.vite/manifest.json +44 -44
- phoenix/server/static/assets/{components-XAeml0-1.js → components-B7lK-RgC.js} +396 -314
- phoenix/server/static/assets/{index-D7EtHUpz.js → index-CpePoyTU.js} +11 -2
- phoenix/server/static/assets/{pages-CPfaxiKa.js → pages-CnVg_GUi.js} +518 -482
- phoenix/server/static/assets/{vendor-arizeai-4fVwwnrI.js → vendor-arizeai-CXCKGfvH.js} +3 -3
- phoenix/server/static/assets/vendor-codemirror-DWr46-WB.js +27 -0
- phoenix/server/static/assets/{vendor-recharts-w6bSawXG.js → vendor-recharts-0Yf6lanX.js} +1 -1
- phoenix/server/static/assets/{vendor-shiki-CplrhwOk.js → vendor-shiki-Caei6iKO.js} +1 -1
- phoenix/server/static/assets/vendor-uWG2jYEi.js +936 -0
- phoenix/version.py +1 -1
- phoenix/server/static/assets/vendor-DhvamIr8.js +0 -939
- phoenix/server/static/assets/vendor-codemirror-DRfFHb57.js +0 -33
- {arize_phoenix-11.10.0.dist-info → arize_phoenix-11.11.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-11.10.0.dist-info → arize_phoenix-11.11.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-11.10.0.dist-info → arize_phoenix-11.11.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-11.10.0.dist-info → arize_phoenix-11.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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=0NvGC949F8-jU5CThwqqkP5g0LPLHwkhtLR2cEkRUTU,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
|
|
@@ -112,7 +112,7 @@ phoenix/server/api/auth.py,sha256=AyYhnZIbY9ALVjg2K6aC2UXSa3Pva5GVDBXyaZ3nD3o,27
|
|
|
112
112
|
phoenix/server/api/context.py,sha256=mqsq_8Ru50e-PxKWNTzh9zptb1PFjYFUf58uW59UYL0,8996
|
|
113
113
|
phoenix/server/api/exceptions.py,sha256=E2W0x63CBzc0CoQPptrLr9nZxPF9zIP8MCJ3RuJMddw,1322
|
|
114
114
|
phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
|
|
115
|
-
phoenix/server/api/queries.py,sha256=
|
|
115
|
+
phoenix/server/api/queries.py,sha256=EVNKWanVbjcPIfG8zW8_p3bBkLxmNuBbS87NUdW68z4,46194
|
|
116
116
|
phoenix/server/api/schema.py,sha256=fcs36xQwFF_Qe41_5cWR8wYpDvOrnbcyTeo5WNMbDsA,1702
|
|
117
117
|
phoenix/server/api/subscriptions.py,sha256=ZOGNsLVr5TNjCWgbzO7Eq6Ls_NRdJH9AxC0cW_v0vhM,25332
|
|
118
118
|
phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,833
|
|
@@ -176,8 +176,8 @@ phoenix/server/api/helpers/__init__.py,sha256=m2-xaSPqUiSs91k62JaRDjFNfl-1byxBfY
|
|
|
176
176
|
phoenix/server/api/helpers/annotations.py,sha256=9gMXKpMTfWEChoSCnvdWYuyB0hlSnNOp-qUdar9Vono,262
|
|
177
177
|
phoenix/server/api/helpers/dataset_helpers.py,sha256=3bdGBoUzqrtg-sr5p2wpQLOU6dhg_3TKFHNeJj8p0TU,9155
|
|
178
178
|
phoenix/server/api/helpers/experiment_run_filters.py,sha256=DOnVwrmn39eAkk2mwuZP8kIcAnR5jrOgllEwWSjsw94,29893
|
|
179
|
-
phoenix/server/api/helpers/playground_clients.py,sha256=
|
|
180
|
-
phoenix/server/api/helpers/playground_registry.py,sha256=
|
|
179
|
+
phoenix/server/api/helpers/playground_clients.py,sha256=Fq4DNVIdnCiiVt0bh5mrZ7dJb2oOQcLjTttfq0Wcuv0,73589
|
|
180
|
+
phoenix/server/api/helpers/playground_registry.py,sha256=n0v4-KnvZJxeaEwOla5qBbnOQjSWznKmMhZnh9ziJt0,2584
|
|
181
181
|
phoenix/server/api/helpers/playground_spans.py,sha256=QpXwPl_fFNwm_iA1A77XApUyXMl1aDmonw8aXuNZ_4k,17132
|
|
182
182
|
phoenix/server/api/helpers/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
183
183
|
phoenix/server/api/helpers/prompts/models.py,sha256=nlPtLZaGcHfWNRR0iNRaBUv8eoKOnoGqRm6zadrTt0I,23547
|
|
@@ -218,6 +218,7 @@ phoenix/server/api/input_types/PerformanceMetricInput.py,sha256=4SG5AymxV2dMeCrK
|
|
|
218
218
|
phoenix/server/api/input_types/ProjectFilter.py,sha256=w3IimMXcNmMkrQ6h89-Yx6uh21r2wj1jci1nPZEDEhQ,260
|
|
219
219
|
phoenix/server/api/input_types/ProjectSessionSort.py,sha256=KZzEtOMpcxtP11brL4fXUAY_RLAK-Ul4luAWwVKnQgY,1043
|
|
220
220
|
phoenix/server/api/input_types/ProjectSort.py,sha256=ZTT-InFB6NvInDwLuivyHe9PxR5nsmQ8aXCHAPjZm9k,329
|
|
221
|
+
phoenix/server/api/input_types/PromptFilter.py,sha256=f2F7fDlYRsNJp_rKkmvpgUt9rfgr_e-dyZxuHX8YfkU,256
|
|
221
222
|
phoenix/server/api/input_types/PromptTemplateOptions.py,sha256=8ZJdH1F9fExcdH9dF8SJ29WycCvtEpK-Z6dZwFO7KgQ,232
|
|
222
223
|
phoenix/server/api/input_types/PromptVersionInput.py,sha256=n6zBeSkK8ZFRHTjtVx4BK--azZIxXeYETa6Cufcet2I,3743
|
|
223
224
|
phoenix/server/api/input_types/SpanAnnotationFilter.py,sha256=-djfIXYCxV6sV3GPOZQUV0SPfiWDhRlTORfeQ7tCBgQ,2671
|
|
@@ -308,7 +309,7 @@ phoenix/server/api/types/ExperimentRun.py,sha256=_fcwDLuURV0yviOlkjWAgJJwcCPdz-x
|
|
|
308
309
|
phoenix/server/api/types/ExperimentRunAnnotation.py,sha256=YGw5zIbjRXUK3zH475DnEeg4SDNGOmdxtuUVkzGw1E8,1734
|
|
309
310
|
phoenix/server/api/types/ExportedFile.py,sha256=e3GTn7B5LgsTbqiwjhMCQH7VsiqXitrBO4aCMS1lHsg,163
|
|
310
311
|
phoenix/server/api/types/Functionality.py,sha256=zDDl2bANIqjwfooSOHg-VQk6-wQy05mREwjV_-VbSIg,262
|
|
311
|
-
phoenix/server/api/types/GenerativeModel.py,sha256=
|
|
312
|
+
phoenix/server/api/types/GenerativeModel.py,sha256=HHpM2aznlmOWeZY77NzTYOcB9iIvWnfbjNUxIOJtvIQ,7688
|
|
312
313
|
phoenix/server/api/types/GenerativeProvider.py,sha256=blXHIzZwe-xlu3-iF2dexvqnb4xxiD2XynS4Vw0iLx4,6750
|
|
313
314
|
phoenix/server/api/types/Identifier.py,sha256=n3rxpoKNCwEvZu7QY8yr7g3AW2mU-U62BxFXYaiHLKk,306
|
|
314
315
|
phoenix/server/api/types/InferenceModel.py,sha256=VWX7eKehFxXp4i_QW_dJAGzeOvh418VUt15AaT_Cv68,8090
|
|
@@ -320,7 +321,7 @@ phoenix/server/api/types/ModelInterface.py,sha256=Qe7H23wDb_Q2-HmeY2t0R5Jsn4aAfY
|
|
|
320
321
|
phoenix/server/api/types/NumericRange.py,sha256=afEjgF97Go_OvmjMggbPBt-zGM8IONewAyEiKEHRds0,192
|
|
321
322
|
phoenix/server/api/types/PerformanceMetric.py,sha256=KFkmJDqP43eDUtARQOUqR7NYcxvL6Vh2uisHWU6H3ko,387
|
|
322
323
|
phoenix/server/api/types/PlaygroundModel.py,sha256=IqJFxsAAJMRyaFI9ryI3GQrpFOJ5Llf6kIutEO-tFvM,321
|
|
323
|
-
phoenix/server/api/types/Project.py,sha256=
|
|
324
|
+
phoenix/server/api/types/Project.py,sha256=wL4jihXtyWjxAtSyRXBpz0DTH-vWZZBMhQFzBBFkl2A,69628
|
|
324
325
|
phoenix/server/api/types/ProjectSession.py,sha256=uwqTsDTfSGz13AvP-cwS_mJR5JZ1lHqu10ungbl7g5s,6245
|
|
325
326
|
phoenix/server/api/types/ProjectTraceRetentionPolicy.py,sha256=tYy2kgalPDyuaYZr0VUHjH0YpXaiF_QOzg5yfaV_c7c,3782
|
|
326
327
|
phoenix/server/api/types/Prompt.py,sha256=ccP4eq1e38xbF0afclGWLOuDpBVpNbJ3AOSRClF8yFQ,4955
|
|
@@ -387,17 +388,17 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
387
388
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
388
389
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
389
390
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
390
|
-
phoenix/server/static/.vite/manifest.json,sha256=
|
|
391
|
-
phoenix/server/static/assets/components-
|
|
392
|
-
phoenix/server/static/assets/index-
|
|
393
|
-
phoenix/server/static/assets/pages-
|
|
391
|
+
phoenix/server/static/.vite/manifest.json,sha256=FDZDQSSV67fIjs_K97eQRfjkSjyK76N2JPn2lzPBVPo,2165
|
|
392
|
+
phoenix/server/static/assets/components-B7lK-RgC.js,sha256=u8Q0_GhFNtht2Js6HZflznlkqdT2FF3rUUFwq3pYZcU,630858
|
|
393
|
+
phoenix/server/static/assets/index-CpePoyTU.js,sha256=LH2IdynxWg7bTImdqHOzUfNtTN9qd5XjFF0nINOdSlM,63028
|
|
394
|
+
phoenix/server/static/assets/pages-CnVg_GUi.js,sha256=V9PbqoJu_CNMRy-4qCeLNGhMnGplEzsRDVHb4-ca9ZI,1198572
|
|
394
395
|
phoenix/server/static/assets/vendor-CqDb5u4o.css,sha256=zIyFiNJKxMaQk8AvtLgt1rR01oO10d1MFndSDKH9Clw,5517
|
|
395
|
-
phoenix/server/static/assets/vendor-
|
|
396
|
-
phoenix/server/static/assets/vendor-
|
|
397
|
-
phoenix/server/static/assets/vendor-
|
|
398
|
-
phoenix/server/static/assets/vendor-
|
|
399
|
-
phoenix/server/static/assets/vendor-shiki-CplrhwOk.js,sha256=ISu7sYmhh_FDTmnBN-icbipm6fa2InqOeQTo2JFr3LI,8980312
|
|
396
|
+
phoenix/server/static/assets/vendor-arizeai-CXCKGfvH.js,sha256=TbpgJ-co-S2Op0E39YiNpafbzoe1OucxLp6g6-wbQNg,151750
|
|
397
|
+
phoenix/server/static/assets/vendor-codemirror-DWr46-WB.js,sha256=DrigLnd9LH-JjswljzWQD1jU2OZ7MaCdwS4u5MtVUh0,553698
|
|
398
|
+
phoenix/server/static/assets/vendor-recharts-0Yf6lanX.js,sha256=56vcRJWCY3T1Qr1H-tWgKVPhoG_tA2iwPm_KA9whHVY,231651
|
|
399
|
+
phoenix/server/static/assets/vendor-shiki-Caei6iKO.js,sha256=oprmwuXapui4i3qdoe4wLxgn0Iotq2xDRG8RwzROPco,8980312
|
|
400
400
|
phoenix/server/static/assets/vendor-three-C5WAXd5r.js,sha256=ELkg06u70N7h8oFmvqdoHyPuUf9VgGEWeT4LKFx4VWo,620975
|
|
401
|
+
phoenix/server/static/assets/vendor-uWG2jYEi.js,sha256=Pm6mq_o91KvR7cdHB3-rW3E8GVE-H-N1Cp0x1XLDJic,2681983
|
|
401
402
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
402
403
|
phoenix/server/templates/index.html,sha256=3VMDmbxYwo3OoqiQyFojU6JaMLKr5k8rITacYS7HTbs,6922
|
|
403
404
|
phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -437,9 +438,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
|
|
|
437
438
|
phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
|
|
438
439
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
439
440
|
phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
|
|
440
|
-
arize_phoenix-11.
|
|
441
|
-
arize_phoenix-11.
|
|
442
|
-
arize_phoenix-11.
|
|
443
|
-
arize_phoenix-11.
|
|
444
|
-
arize_phoenix-11.
|
|
445
|
-
arize_phoenix-11.
|
|
441
|
+
arize_phoenix-11.11.0.dist-info/METADATA,sha256=EeNefvxStTYsByuT4E2kzd4DsmNHCrHNalV-nFxAV9A,30851
|
|
442
|
+
arize_phoenix-11.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
443
|
+
arize_phoenix-11.11.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
|
|
444
|
+
arize_phoenix-11.11.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
445
|
+
arize_phoenix-11.11.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
446
|
+
arize_phoenix-11.11.0.dist-info/RECORD,,
|
|
@@ -20,7 +20,7 @@ from openinference.semconv.trace import (
|
|
|
20
20
|
)
|
|
21
21
|
from strawberry import UNSET
|
|
22
22
|
from strawberry.scalars import JSON as JSONScalarType
|
|
23
|
-
from typing_extensions import TypeAlias, assert_never
|
|
23
|
+
from typing_extensions import TypeAlias, assert_never, override
|
|
24
24
|
|
|
25
25
|
from phoenix.config import getenv
|
|
26
26
|
from phoenix.evals.models.rate_limiters import (
|
|
@@ -437,9 +437,9 @@ class OpenAIBaseStreamingClient(PlaygroundStreamingClient):
|
|
|
437
437
|
if role is ChatCompletionMessageRole.TOOL:
|
|
438
438
|
if tool_call_id is None:
|
|
439
439
|
raise ValueError("tool_call_id is required for tool messages")
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
440
|
+
return ChatCompletionToolMessageParam(
|
|
441
|
+
{"content": content, "role": "tool", "tool_call_id": tool_call_id}
|
|
442
|
+
)
|
|
443
443
|
assert_never(role)
|
|
444
444
|
|
|
445
445
|
def to_openai_tool_call_param(
|
|
@@ -1140,27 +1140,28 @@ class OpenAIStreamingClient(OpenAIBaseStreamingClient):
|
|
|
1140
1140
|
self._attributes[LLM_SYSTEM] = OpenInferenceLLMSystemValues.OPENAI.value
|
|
1141
1141
|
|
|
1142
1142
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
class
|
|
1143
|
+
_OPENAI_REASONING_MODELS = [
|
|
1144
|
+
"o1",
|
|
1145
|
+
"o1-pro",
|
|
1146
|
+
"o1-2024-12-17",
|
|
1147
|
+
"o1-pro-2025-03-19",
|
|
1148
|
+
"o1-mini",
|
|
1149
|
+
"o1-mini-2024-09-12",
|
|
1150
|
+
"o1-preview",
|
|
1151
|
+
"o1-preview-2024-09-12",
|
|
1152
|
+
"o3",
|
|
1153
|
+
"o3-pro",
|
|
1154
|
+
"o3-2025-04-16",
|
|
1155
|
+
"o3-mini",
|
|
1156
|
+
"o3-mini-2025-01-31",
|
|
1157
|
+
"o4-mini",
|
|
1158
|
+
"o4-mini-2025-04-16",
|
|
1159
|
+
]
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
class OpenAIReasoningReasoningModelsMixin:
|
|
1163
|
+
"""Mixin class for OpenAI-style reasoning model clients (o1, o3 series)."""
|
|
1164
|
+
|
|
1164
1165
|
@classmethod
|
|
1165
1166
|
def supported_invocation_parameters(cls) -> list[InvocationParameter]:
|
|
1166
1167
|
return [
|
|
@@ -1191,6 +1192,16 @@ class OpenAIReasoningStreamingClient(OpenAIStreamingClient):
|
|
|
1191
1192
|
),
|
|
1192
1193
|
]
|
|
1193
1194
|
|
|
1195
|
+
|
|
1196
|
+
@register_llm_client(
|
|
1197
|
+
provider_key=GenerativeProviderKey.OPENAI,
|
|
1198
|
+
model_names=_OPENAI_REASONING_MODELS,
|
|
1199
|
+
)
|
|
1200
|
+
class OpenAIReasoningNonStreamingClient(
|
|
1201
|
+
OpenAIReasoningReasoningModelsMixin,
|
|
1202
|
+
OpenAIStreamingClient,
|
|
1203
|
+
):
|
|
1204
|
+
@override
|
|
1194
1205
|
async def chat_completion_create(
|
|
1195
1206
|
self,
|
|
1196
1207
|
messages: list[
|
|
@@ -1283,46 +1294,11 @@ class OpenAIReasoningStreamingClient(OpenAIStreamingClient):
|
|
|
1283
1294
|
if role is ChatCompletionMessageRole.TOOL:
|
|
1284
1295
|
if tool_call_id is None:
|
|
1285
1296
|
raise ValueError("tool_call_id is required for tool messages")
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1297
|
+
return ChatCompletionToolMessageParam(
|
|
1298
|
+
{"content": content, "role": "tool", "tool_call_id": tool_call_id}
|
|
1299
|
+
)
|
|
1289
1300
|
assert_never(role)
|
|
1290
1301
|
|
|
1291
|
-
@staticmethod
|
|
1292
|
-
def _llm_token_counts(usage: "CompletionUsage") -> Iterator[tuple[str, Any]]:
|
|
1293
|
-
yield LLM_TOKEN_COUNT_PROMPT, usage.prompt_tokens
|
|
1294
|
-
yield LLM_TOKEN_COUNT_COMPLETION, usage.completion_tokens
|
|
1295
|
-
yield LLM_TOKEN_COUNT_TOTAL, usage.total_tokens
|
|
1296
|
-
|
|
1297
|
-
if hasattr(usage, "prompt_tokens_details") and usage.prompt_tokens_details is not None:
|
|
1298
|
-
prompt_details = usage.prompt_tokens_details
|
|
1299
|
-
if (
|
|
1300
|
-
hasattr(prompt_details, "cached_tokens")
|
|
1301
|
-
and prompt_details.cached_tokens is not None
|
|
1302
|
-
):
|
|
1303
|
-
yield LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ, prompt_details.cached_tokens
|
|
1304
|
-
if hasattr(prompt_details, "audio_tokens") and prompt_details.audio_tokens is not None:
|
|
1305
|
-
yield LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO, prompt_details.audio_tokens
|
|
1306
|
-
|
|
1307
|
-
if (
|
|
1308
|
-
hasattr(usage, "completion_tokens_details")
|
|
1309
|
-
and usage.completion_tokens_details is not None
|
|
1310
|
-
):
|
|
1311
|
-
completion_details = usage.completion_tokens_details
|
|
1312
|
-
if (
|
|
1313
|
-
hasattr(completion_details, "reasoning_tokens")
|
|
1314
|
-
and completion_details.reasoning_tokens is not None
|
|
1315
|
-
):
|
|
1316
|
-
yield (
|
|
1317
|
-
LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING,
|
|
1318
|
-
completion_details.reasoning_tokens,
|
|
1319
|
-
)
|
|
1320
|
-
if (
|
|
1321
|
-
hasattr(completion_details, "audio_tokens")
|
|
1322
|
-
and completion_details.audio_tokens is not None
|
|
1323
|
-
):
|
|
1324
|
-
yield LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO, completion_details.audio_tokens
|
|
1325
|
-
|
|
1326
1302
|
|
|
1327
1303
|
@register_llm_client(
|
|
1328
1304
|
provider_key=GenerativeProviderKey.AZURE_OPENAI,
|
|
@@ -1376,6 +1352,113 @@ class AzureOpenAIStreamingClient(OpenAIBaseStreamingClient):
|
|
|
1376
1352
|
self._attributes[LLM_SYSTEM] = OpenInferenceLLMSystemValues.OPENAI.value
|
|
1377
1353
|
|
|
1378
1354
|
|
|
1355
|
+
@register_llm_client(
|
|
1356
|
+
provider_key=GenerativeProviderKey.AZURE_OPENAI,
|
|
1357
|
+
model_names=_OPENAI_REASONING_MODELS,
|
|
1358
|
+
)
|
|
1359
|
+
class AzureOpenAIReasoningNonStreamingClient(
|
|
1360
|
+
OpenAIReasoningReasoningModelsMixin,
|
|
1361
|
+
AzureOpenAIStreamingClient,
|
|
1362
|
+
):
|
|
1363
|
+
@override
|
|
1364
|
+
async def chat_completion_create(
|
|
1365
|
+
self,
|
|
1366
|
+
messages: list[
|
|
1367
|
+
tuple[ChatCompletionMessageRole, str, Optional[str], Optional[list[JSONScalarType]]]
|
|
1368
|
+
],
|
|
1369
|
+
tools: list[JSONScalarType],
|
|
1370
|
+
**invocation_parameters: Any,
|
|
1371
|
+
) -> AsyncIterator[ChatCompletionChunk]:
|
|
1372
|
+
from openai import NOT_GIVEN
|
|
1373
|
+
|
|
1374
|
+
# Convert standard messages to OpenAI messages
|
|
1375
|
+
openai_messages = []
|
|
1376
|
+
for message in messages:
|
|
1377
|
+
openai_message = self.to_openai_chat_completion_param(*message)
|
|
1378
|
+
if openai_message is not None:
|
|
1379
|
+
openai_messages.append(openai_message)
|
|
1380
|
+
|
|
1381
|
+
throttled_create = self.rate_limiter._alimit(self.client.chat.completions.create)
|
|
1382
|
+
response = await throttled_create(
|
|
1383
|
+
messages=openai_messages,
|
|
1384
|
+
model=self.model_name,
|
|
1385
|
+
stream=False,
|
|
1386
|
+
tools=tools or NOT_GIVEN,
|
|
1387
|
+
**invocation_parameters,
|
|
1388
|
+
)
|
|
1389
|
+
|
|
1390
|
+
if response.usage is not None:
|
|
1391
|
+
self._attributes.update(dict(self._llm_token_counts(response.usage)))
|
|
1392
|
+
|
|
1393
|
+
choice = response.choices[0]
|
|
1394
|
+
if choice.message.content:
|
|
1395
|
+
yield TextChunk(content=choice.message.content)
|
|
1396
|
+
|
|
1397
|
+
if choice.message.tool_calls:
|
|
1398
|
+
for tool_call in choice.message.tool_calls:
|
|
1399
|
+
yield ToolCallChunk(
|
|
1400
|
+
id=tool_call.id,
|
|
1401
|
+
function=FunctionCallChunk(
|
|
1402
|
+
name=tool_call.function.name,
|
|
1403
|
+
arguments=tool_call.function.arguments,
|
|
1404
|
+
),
|
|
1405
|
+
)
|
|
1406
|
+
|
|
1407
|
+
def to_openai_chat_completion_param(
|
|
1408
|
+
self,
|
|
1409
|
+
role: ChatCompletionMessageRole,
|
|
1410
|
+
content: JSONScalarType,
|
|
1411
|
+
tool_call_id: Optional[str] = None,
|
|
1412
|
+
tool_calls: Optional[list[JSONScalarType]] = None,
|
|
1413
|
+
) -> Optional["ChatCompletionMessageParam"]:
|
|
1414
|
+
from openai.types.chat import (
|
|
1415
|
+
ChatCompletionAssistantMessageParam,
|
|
1416
|
+
ChatCompletionDeveloperMessageParam,
|
|
1417
|
+
ChatCompletionToolMessageParam,
|
|
1418
|
+
ChatCompletionUserMessageParam,
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
if role is ChatCompletionMessageRole.USER:
|
|
1422
|
+
return ChatCompletionUserMessageParam(
|
|
1423
|
+
{
|
|
1424
|
+
"content": content,
|
|
1425
|
+
"role": "user",
|
|
1426
|
+
}
|
|
1427
|
+
)
|
|
1428
|
+
if role is ChatCompletionMessageRole.SYSTEM:
|
|
1429
|
+
return ChatCompletionDeveloperMessageParam(
|
|
1430
|
+
{
|
|
1431
|
+
"content": content,
|
|
1432
|
+
"role": "developer",
|
|
1433
|
+
}
|
|
1434
|
+
)
|
|
1435
|
+
if role is ChatCompletionMessageRole.AI:
|
|
1436
|
+
if tool_calls is None:
|
|
1437
|
+
return ChatCompletionAssistantMessageParam(
|
|
1438
|
+
{
|
|
1439
|
+
"content": content,
|
|
1440
|
+
"role": "assistant",
|
|
1441
|
+
}
|
|
1442
|
+
)
|
|
1443
|
+
else:
|
|
1444
|
+
return ChatCompletionAssistantMessageParam(
|
|
1445
|
+
{
|
|
1446
|
+
"content": content,
|
|
1447
|
+
"role": "assistant",
|
|
1448
|
+
"tool_calls": [
|
|
1449
|
+
self.to_openai_tool_call_param(tool_call) for tool_call in tool_calls
|
|
1450
|
+
],
|
|
1451
|
+
}
|
|
1452
|
+
)
|
|
1453
|
+
if role is ChatCompletionMessageRole.TOOL:
|
|
1454
|
+
if tool_call_id is None:
|
|
1455
|
+
raise ValueError("tool_call_id is required for tool messages")
|
|
1456
|
+
return ChatCompletionToolMessageParam(
|
|
1457
|
+
{"content": content, "role": "tool", "tool_call_id": tool_call_id}
|
|
1458
|
+
)
|
|
1459
|
+
assert_never(role)
|
|
1460
|
+
|
|
1461
|
+
|
|
1379
1462
|
@register_llm_client(
|
|
1380
1463
|
provider_key=GenerativeProviderKey.ANTHROPIC,
|
|
1381
1464
|
model_names=[
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence, Union
|
|
2
2
|
|
|
3
3
|
from phoenix.server.api.types.GenerativeProvider import GenerativeProviderKey
|
|
4
4
|
|
|
@@ -59,7 +59,7 @@ PLAYGROUND_CLIENT_REGISTRY: PlaygroundClientRegistry = PlaygroundClientRegistry(
|
|
|
59
59
|
|
|
60
60
|
def register_llm_client(
|
|
61
61
|
provider_key: GenerativeProviderKey,
|
|
62
|
-
model_names:
|
|
62
|
+
model_names: Sequence[ModelName],
|
|
63
63
|
) -> Callable[[type["PlaygroundStreamingClient"]], type["PlaygroundStreamingClient"]]:
|
|
64
64
|
def decorator(cls: type["PlaygroundStreamingClient"]) -> type["PlaygroundStreamingClient"]:
|
|
65
65
|
provider_registry = PLAYGROUND_CLIENT_REGISTRY._registry.setdefault(provider_key, {})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
import strawberry
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@strawberry.enum
|
|
7
|
+
class PromptFilterColumn(Enum):
|
|
8
|
+
name = "name"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@strawberry.input(description="The filter key and value for prompt connections")
|
|
12
|
+
class PromptFilter:
|
|
13
|
+
col: PromptFilterColumn
|
|
14
|
+
value: str
|
phoenix/server/api/queries.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import Iterable, Iterator, Optional, Union
|
|
4
|
+
from typing import Iterable, Iterator, Optional, Union
|
|
5
|
+
from typing import cast as type_cast
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
import numpy.typing as npt
|
|
8
9
|
import strawberry
|
|
9
|
-
from sqlalchemy import and_, distinct, func, select, text
|
|
10
|
+
from sqlalchemy import String, and_, cast, distinct, func, select, text
|
|
10
11
|
from sqlalchemy.orm import joinedload
|
|
11
12
|
from starlette.authentication import UnauthenticatedUser
|
|
12
13
|
from strawberry import ID, UNSET
|
|
@@ -41,6 +42,7 @@ from phoenix.server.api.input_types.DatasetSort import DatasetSort
|
|
|
41
42
|
from phoenix.server.api.input_types.InvocationParameters import InvocationParameter
|
|
42
43
|
from phoenix.server.api.input_types.ProjectFilter import ProjectFilter
|
|
43
44
|
from phoenix.server.api.input_types.ProjectSort import ProjectColumn, ProjectSort
|
|
45
|
+
from phoenix.server.api.input_types.PromptFilter import PromptFilter
|
|
44
46
|
from phoenix.server.api.types.AnnotationConfig import AnnotationConfig, to_gql_annotation_config
|
|
45
47
|
from phoenix.server.api.types.Cluster import Cluster, to_gql_clusters
|
|
46
48
|
from phoenix.server.api.types.Dataset import Dataset, to_gql_dataset
|
|
@@ -728,6 +730,7 @@ class Query:
|
|
|
728
730
|
last: Optional[int] = UNSET,
|
|
729
731
|
after: Optional[CursorString] = UNSET,
|
|
730
732
|
before: Optional[CursorString] = UNSET,
|
|
733
|
+
filter: Optional[PromptFilter] = UNSET,
|
|
731
734
|
) -> Connection[Prompt]:
|
|
732
735
|
args = ConnectionArgs(
|
|
733
736
|
first=first,
|
|
@@ -736,6 +739,14 @@ class Query:
|
|
|
736
739
|
before=before if isinstance(before, CursorString) else None,
|
|
737
740
|
)
|
|
738
741
|
stmt = select(models.Prompt)
|
|
742
|
+
if filter:
|
|
743
|
+
column = getattr(models.Prompt, filter.col.value)
|
|
744
|
+
# Cast Identifier columns to String for ilike operations
|
|
745
|
+
if filter.col.value == "name":
|
|
746
|
+
column = cast(column, String)
|
|
747
|
+
stmt = stmt.where(column.ilike(f"%{filter.value}%")).order_by(
|
|
748
|
+
models.Prompt.updated_at.desc()
|
|
749
|
+
)
|
|
739
750
|
async with info.context.db() as session:
|
|
740
751
|
orm_prompts = await session.stream_scalars(stmt)
|
|
741
752
|
data = [to_gql_prompt_from_orm(orm_prompt) async for orm_prompt in orm_prompts]
|
|
@@ -994,7 +1005,7 @@ class Query:
|
|
|
994
1005
|
""").bindparams(nspname=nspname)
|
|
995
1006
|
try:
|
|
996
1007
|
async with info.context.db() as session:
|
|
997
|
-
stats =
|
|
1008
|
+
stats = type_cast(Iterable[tuple[str, int]], await session.execute(stmt))
|
|
998
1009
|
except Exception:
|
|
999
1010
|
# TODO: temporary workaround until we can reproduce the error
|
|
1000
1011
|
return []
|
|
@@ -6,14 +6,19 @@ import strawberry
|
|
|
6
6
|
from openinference.semconv.trace import OpenInferenceLLMProviderValues
|
|
7
7
|
from sqlalchemy import inspect
|
|
8
8
|
from strawberry.relay import Node, NodeID
|
|
9
|
+
from strawberry.relay.types import GlobalID
|
|
9
10
|
from strawberry.types import Info
|
|
10
|
-
from
|
|
11
|
+
from strawberry.types.unset import UNSET
|
|
12
|
+
from typing_extensions import TypeAlias, assert_never
|
|
11
13
|
|
|
12
14
|
from phoenix.db import models
|
|
13
15
|
from phoenix.server.api.context import Context
|
|
16
|
+
from phoenix.server.api.exceptions import BadRequest
|
|
17
|
+
from phoenix.server.api.input_types.TimeRange import TimeRange
|
|
14
18
|
from phoenix.server.api.types.CostBreakdown import CostBreakdown
|
|
15
19
|
from phoenix.server.api.types.GenerativeProvider import GenerativeProviderKey
|
|
16
20
|
from phoenix.server.api.types.ModelInterface import ModelInterface
|
|
21
|
+
from phoenix.server.api.types.node import from_global_id
|
|
17
22
|
from phoenix.server.api.types.SpanCostDetailSummaryEntry import SpanCostDetailSummaryEntry
|
|
18
23
|
from phoenix.server.api.types.SpanCostSummary import SpanCostSummary
|
|
19
24
|
from phoenix.server.api.types.TokenPrice import TokenKind, TokenPrice
|
|
@@ -25,6 +30,11 @@ class GenerativeModelKind(Enum):
|
|
|
25
30
|
BUILT_IN = "BUILT_IN"
|
|
26
31
|
|
|
27
32
|
|
|
33
|
+
ProjectId: TypeAlias = int
|
|
34
|
+
TimeRangeKey: TypeAlias = tuple[Optional[datetime], Optional[datetime]]
|
|
35
|
+
CachedCostSummaryKey: TypeAlias = tuple[Optional[ProjectId], TimeRangeKey]
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
@strawberry.type
|
|
29
39
|
class GenerativeModel(Node, ModelInterface):
|
|
30
40
|
id_attr: NodeID[int]
|
|
@@ -37,6 +47,18 @@ class GenerativeModel(Node, ModelInterface):
|
|
|
37
47
|
provider_key: Optional[GenerativeProviderKey]
|
|
38
48
|
costs: strawberry.Private[Optional[list[models.TokenPrice]]] = None
|
|
39
49
|
start_time: Optional[datetime] = None
|
|
50
|
+
cached_cost_summary: strawberry.Private[
|
|
51
|
+
Optional[dict[CachedCostSummaryKey, SpanCostSummary]]
|
|
52
|
+
] = None
|
|
53
|
+
|
|
54
|
+
def add_cached_cost_summary(
|
|
55
|
+
self, project_id: Optional[int], time_range: TimeRange, cost_summary: SpanCostSummary
|
|
56
|
+
) -> None:
|
|
57
|
+
if self.cached_cost_summary is None:
|
|
58
|
+
self.cached_cost_summary = {}
|
|
59
|
+
time_range_key = (time_range.start, time_range.end) if time_range else (None, None)
|
|
60
|
+
cache_key = (project_id, time_range_key)
|
|
61
|
+
self.cached_cost_summary[cache_key] = cost_summary
|
|
40
62
|
|
|
41
63
|
@strawberry.field
|
|
42
64
|
async def token_prices(self) -> list[TokenPrice]:
|
|
@@ -55,7 +77,28 @@ class GenerativeModel(Node, ModelInterface):
|
|
|
55
77
|
return token_prices
|
|
56
78
|
|
|
57
79
|
@strawberry.field
|
|
58
|
-
async def cost_summary(
|
|
80
|
+
async def cost_summary(
|
|
81
|
+
self,
|
|
82
|
+
info: Info[Context, None],
|
|
83
|
+
project_id: Optional[GlobalID] = UNSET,
|
|
84
|
+
time_range: Optional[TimeRange] = UNSET,
|
|
85
|
+
) -> SpanCostSummary:
|
|
86
|
+
if self.cached_cost_summary is not None:
|
|
87
|
+
time_range_key = (time_range.start, time_range.end) if time_range else (None, None)
|
|
88
|
+
project_rowid: Optional[int] = None
|
|
89
|
+
if project_id:
|
|
90
|
+
type_name, project_rowid = from_global_id(project_id)
|
|
91
|
+
if type_name != models.Project.__name__:
|
|
92
|
+
raise BadRequest("Invalid Project ID")
|
|
93
|
+
cache_key = (project_rowid, time_range_key)
|
|
94
|
+
if cache_key in self.cached_cost_summary:
|
|
95
|
+
return self.cached_cost_summary[cache_key]
|
|
96
|
+
|
|
97
|
+
if time_range or project_id:
|
|
98
|
+
raise BadRequest(
|
|
99
|
+
"Cost summaries for specific projects or time ranges are not yet implemented"
|
|
100
|
+
)
|
|
101
|
+
|
|
59
102
|
loader = info.context.data_loaders.span_cost_summary_by_generative_model
|
|
60
103
|
summary = await loader.load(self.id_attr)
|
|
61
104
|
return SpanCostSummary(
|
|
@@ -98,7 +141,9 @@ class GenerativeModel(Node, ModelInterface):
|
|
|
98
141
|
return await info.context.data_loaders.last_used_times_by_generative_model_id.load(model_id)
|
|
99
142
|
|
|
100
143
|
|
|
101
|
-
def to_gql_generative_model(
|
|
144
|
+
def to_gql_generative_model(
|
|
145
|
+
model: models.GenerativeModel,
|
|
146
|
+
) -> GenerativeModel:
|
|
102
147
|
costs_are_loaded = isinstance(inspect(model).attrs.token_prices.loaded_value, list)
|
|
103
148
|
name_pattern = model.name_pattern.pattern
|
|
104
149
|
assert isinstance(name_pattern, str)
|