arize-phoenix 5.10.0__py3-none-any.whl → 5.12.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-5.10.0.dist-info → arize_phoenix-5.12.0.dist-info}/METADATA +2 -1
- {arize_phoenix-5.10.0.dist-info → arize_phoenix-5.12.0.dist-info}/RECORD +25 -25
- phoenix/config.py +13 -0
- phoenix/db/facilitator.py +3 -2
- phoenix/server/api/helpers/playground_clients.py +64 -77
- phoenix/server/api/helpers/playground_spans.py +6 -0
- phoenix/server/api/mutations/chat_mutations.py +81 -36
- phoenix/server/api/subscriptions.py +156 -58
- phoenix/server/api/types/TemplateLanguage.py +1 -0
- phoenix/server/static/.vite/manifest.json +31 -31
- phoenix/server/static/assets/{components-BXIz9ZO8.js → components-72cQL1d1.js} +95 -95
- phoenix/server/static/assets/{index-DTut7g1y.js → index-BowjltW-.js} +1 -1
- phoenix/server/static/assets/{pages-B8FpJuXu.js → pages-DFAkBAUh.js} +339 -271
- phoenix/server/static/assets/{vendor-BX8_Znqy.js → vendor-DexmGnha.js} +150 -150
- phoenix/server/static/assets/{vendor-arizeai-CtHir-Ua.js → vendor-arizeai--Q3ol330.js} +28 -28
- phoenix/server/static/assets/{vendor-codemirror-DLlGiguX.js → vendor-codemirror-B4bYvWa6.js} +1 -1
- phoenix/server/static/assets/{vendor-recharts-CJRple0d.js → vendor-recharts-B4ZzJhNh.js} +1 -1
- phoenix/trace/span_evaluations.py +4 -3
- phoenix/utilities/json.py +7 -1
- phoenix/utilities/template_formatters.py +18 -0
- phoenix/version.py +1 -1
- {arize_phoenix-5.10.0.dist-info → arize_phoenix-5.12.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-5.10.0.dist-info → arize_phoenix-5.12.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-5.10.0.dist-info → arize_phoenix-5.12.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-5.10.0.dist-info → arize_phoenix-5.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arize-phoenix
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.12.0
|
|
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
|
|
@@ -53,6 +53,7 @@ Requires-Dist: wrapt
|
|
|
53
53
|
Provides-Extra: container
|
|
54
54
|
Requires-Dist: anthropic; extra == 'container'
|
|
55
55
|
Requires-Dist: fast-hdbscan>=0.2.0; extra == 'container'
|
|
56
|
+
Requires-Dist: google-generativeai; extra == 'container'
|
|
56
57
|
Requires-Dist: numba>=0.60.0; extra == 'container'
|
|
57
58
|
Requires-Dist: openai>=1.0.0; extra == 'container'
|
|
58
59
|
Requires-Dist: opentelemetry-exporter-otlp; extra == 'container'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
phoenix/__init__.py,sha256=X3eUEwd2rG8KKWWYVNNDJoqo08ihfjgHhlP29dcdNJE,5481
|
|
2
2
|
phoenix/auth.py,sha256=JpkwJbis2INlIXWcQ-M_Nk5Ln9LBgHMdWNnaAQp0D2w,10940
|
|
3
|
-
phoenix/config.py,sha256=
|
|
3
|
+
phoenix/config.py,sha256=QLaKuc7N3qZsaAzJnsUVyFuWaROYTVlZsVXTr0Re6d8,26191
|
|
4
4
|
phoenix/datetime_utils.py,sha256=iJzNG6YJ6V7_u8B2iA7P2Z26FyxYbOPtx0dhJ7kNDHA,3398
|
|
5
5
|
phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
|
|
6
6
|
phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
7
7
|
phoenix/services.py,sha256=kpW1WL0kiB8XJsO6XycvZVJ-lBkNoenhQ7atCvBoSe8,5365
|
|
8
8
|
phoenix/settings.py,sha256=ht-0oN-sMV6SPXrk7Tu1EZlngpAYkGNLYPhO8DyrdQI,661
|
|
9
|
-
phoenix/version.py,sha256=
|
|
9
|
+
phoenix/version.py,sha256=9dFDHPiqLy0dTf00acCCS0XDudNj7hSnMr9G2iMDI6A,23
|
|
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
|
|
@@ -18,7 +18,7 @@ phoenix/db/alembic.ini,sha256=GIS6HpHaKaJbbuahZg1Rc1D2_QqyCkV9r58wdARGf6w,3262
|
|
|
18
18
|
phoenix/db/bulk_inserter.py,sha256=faNjuwLqqsw4ky8sa4D0h9u5TvEDTOjrccUW89L008E,12725
|
|
19
19
|
phoenix/db/engines.py,sha256=BYFiHFGABtNuCo7OB8fLc46gMxur4rePK_sJxrMrMjA,6508
|
|
20
20
|
phoenix/db/enums.py,sha256=tt7iovXLhVTLZ3_LbHNGgcI44SnNjXfkKtLAZG57T54,428
|
|
21
|
-
phoenix/db/facilitator.py,sha256=
|
|
21
|
+
phoenix/db/facilitator.py,sha256=sAYqzBXYSVBKPTQVYrd7ZmtqMAr1zP9dVJwjfNGW7hc,4207
|
|
22
22
|
phoenix/db/helpers.py,sha256=daKbpY2QhTPo9a_T1xNHKI4WzWHkMmmrGIws7Hw-RZ4,4884
|
|
23
23
|
phoenix/db/migrate.py,sha256=lf6FFGzdyNjwtQ4TFi3mmHNGnnazMtG30PPB2qRF1Xc,2648
|
|
24
24
|
phoenix/db/models.py,sha256=wx2XQgf6qhUO2ZQ4oUENoAguf440qaR0Ea_q1XB1bBU,26484
|
|
@@ -94,7 +94,7 @@ phoenix/server/api/exceptions.py,sha256=TA0JuY2YRnj35qGuMSQ8d0ToHum9gWm9W--3fSKH
|
|
|
94
94
|
phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
|
|
95
95
|
phoenix/server/api/queries.py,sha256=4KJz8TUz3VUTup9MDjr_GoKX0SttWSvHBq2ncWZGxf8,27343
|
|
96
96
|
phoenix/server/api/schema.py,sha256=tHyw2jTbue_-gu0fe9Sw7LUYtzJUCwp9SvccDgOkNPw,1696
|
|
97
|
-
phoenix/server/api/subscriptions.py,sha256=
|
|
97
|
+
phoenix/server/api/subscriptions.py,sha256=nsiByh2rTPc1PS7OrPyuyyfFfYUUXeGmai19IfYP0dA,23201
|
|
98
98
|
phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,833
|
|
99
99
|
phoenix/server/api/dataloaders/__init__.py,sha256=jNYvfXjnZzgA2HWTG7AZdqWGla3ZysBUDUei8Zkz6N8,3290
|
|
100
100
|
phoenix/server/api/dataloaders/annotation_summaries.py,sha256=2sHmIDX7n8tuPeBTs9bMKtlMKWn_Ph9awTZqmwn2Owc,5505
|
|
@@ -125,9 +125,9 @@ phoenix/server/api/dataloaders/cache/__init__.py,sha256=SYoOM9n8FJaMdQarma5d1blu
|
|
|
125
125
|
phoenix/server/api/dataloaders/cache/two_tier_cache.py,sha256=cmo8FUT3E91R139IEzh4yCga-6nTamc5KPXAfMrzNDM,2315
|
|
126
126
|
phoenix/server/api/helpers/__init__.py,sha256=m2-xaSPqUiSs91k62JaRDjFNfl-1byxBfY-m_Vxw16U,272
|
|
127
127
|
phoenix/server/api/helpers/dataset_helpers.py,sha256=14mldZp9to3rr9BdvvoFqEwZHHV_k2e7jPm8q9z2OdQ,6896
|
|
128
|
-
phoenix/server/api/helpers/playground_clients.py,sha256=
|
|
128
|
+
phoenix/server/api/helpers/playground_clients.py,sha256=hIxAM09HexNyKk0HQYbAD-X5axyw7my7B0dhPFzwueo,36383
|
|
129
129
|
phoenix/server/api/helpers/playground_registry.py,sha256=CPLMziFB2wmr-dfbx7VbzO2f8YIG_k5RftzvGXYGQ1w,2570
|
|
130
|
-
phoenix/server/api/helpers/playground_spans.py,sha256=
|
|
130
|
+
phoenix/server/api/helpers/playground_spans.py,sha256=LJjndkVxr6DsfVFerrtMZWlRd5YL5AIbcM2CvFe8ajc,16489
|
|
131
131
|
phoenix/server/api/input_types/AddExamplesToDatasetInput.py,sha256=mIQz0S_z8YdrktKIY6RCvtNJ2yZF9pYvTGgasUsI-54,430
|
|
132
132
|
phoenix/server/api/input_types/AddSpansToDatasetInput.py,sha256=-StSstyMAVrba3tG1U30b-srkKCtu_svflQuSM19iJA,362
|
|
133
133
|
phoenix/server/api/input_types/ChatCompletionInput.py,sha256=g_5ARuwylt-uCVAsGyZPEVtidEQiOhbKakvDQsZumzw,1451
|
|
@@ -164,7 +164,7 @@ phoenix/server/api/input_types/UserRoleInput.py,sha256=xxhFe0ITZOgRVEJbVem_W6F1I
|
|
|
164
164
|
phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
165
165
|
phoenix/server/api/mutations/__init__.py,sha256=1wClieLNA3_Tin4Ah67rkrQvwSSZAdPU0EPsRiUxyAA,1103
|
|
166
166
|
phoenix/server/api/mutations/api_key_mutations.py,sha256=OmPipsmlQIb6DKvAgO58mZUwkYJihlJB2N4lTyeUlAA,6164
|
|
167
|
-
phoenix/server/api/mutations/chat_mutations.py,sha256=
|
|
167
|
+
phoenix/server/api/mutations/chat_mutations.py,sha256=Nz7pz6cTvxub33kW6J0KgYmf7Lhb1ZmzF65KeUqYjiQ,22795
|
|
168
168
|
phoenix/server/api/mutations/dataset_mutations.py,sha256=siwsSmozKRIK8ZhPrfWl-GxKCL4lAmRPms862tG2KXY,27064
|
|
169
169
|
phoenix/server/api/mutations/experiment_mutations.py,sha256=p3CoLAa8nFPa3D759Y2A7De_PVJNGOL98mA3HoZBrRQ,3188
|
|
170
170
|
phoenix/server/api/mutations/export_events_mutations.py,sha256=xoDnVWC7eA_8wNQP0-oyiHojyUZ0EhVVSrsAnztetC0,3993
|
|
@@ -244,7 +244,7 @@ phoenix/server/api/types/SortDir.py,sha256=OUpXhlCzCxPoXSDkJJygEs9Rw9pMymfaZUG5z
|
|
|
244
244
|
phoenix/server/api/types/Span.py,sha256=6GS6MpJ3f8P2LrQUe2TWPrPf7ENxmde_wisQkJguphw,16919
|
|
245
245
|
phoenix/server/api/types/SpanAnnotation.py,sha256=6b5G-b_OoRvDL2ayWk7MkbqarLK-F-pQMx21CpUuNGY,1168
|
|
246
246
|
phoenix/server/api/types/SystemApiKey.py,sha256=2ym8EgsTBIvxx1l9xZ-2YMovz58ZwYb_MaHBTJ9NH2E,166
|
|
247
|
-
phoenix/server/api/types/TemplateLanguage.py,sha256=
|
|
247
|
+
phoenix/server/api/types/TemplateLanguage.py,sha256=6j_0uwO_GZIeCpR7sTOnxySXudT7qBSC6LFsjzbvW1o,160
|
|
248
248
|
phoenix/server/api/types/TimeSeries.py,sha256=IIeGVRFdSMozYXxPg736DW_mKvj4-3WjYSYEnn4UEJc,5241
|
|
249
249
|
phoenix/server/api/types/Trace.py,sha256=1RrdEedlPpNmWkQwosOZ81fabc9-B6PDHTYpr-hZj-Y,3240
|
|
250
250
|
phoenix/server/api/types/TraceAnnotation.py,sha256=OW6A2zr1gomOuG0XQe55dk15XXX2DSM0DzatRbHWH5A,1256
|
|
@@ -273,15 +273,15 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
273
273
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
274
274
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
275
275
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
276
|
-
phoenix/server/static/.vite/manifest.json,sha256=
|
|
277
|
-
phoenix/server/static/assets/components-
|
|
278
|
-
phoenix/server/static/assets/index-
|
|
279
|
-
phoenix/server/static/assets/pages-
|
|
280
|
-
phoenix/server/static/assets/vendor-
|
|
276
|
+
phoenix/server/static/.vite/manifest.json,sha256=MbaJGZ9fqq-pJiyZWU0xbb2FXhmggvZc6DNnIWBeJow,1929
|
|
277
|
+
phoenix/server/static/assets/components-72cQL1d1.js,sha256=wSbuZXCJY152KhyfYItsBNWOU8LqshboNmVlCof-r70,306866
|
|
278
|
+
phoenix/server/static/assets/index-BowjltW-.js,sha256=Zgvw87yavo4A9jEyPQAzFKNBiAFF_SWzZopNsbfwMig,7290
|
|
279
|
+
phoenix/server/static/assets/pages-DFAkBAUh.js,sha256=SMq23Vg-OZSvgSdKOkogJBhNXzlRfyl_oZYKJPyNqvY,636153
|
|
280
|
+
phoenix/server/static/assets/vendor-DexmGnha.js,sha256=sieuT9Y_X4RXRfqPAXRBpPPPwdb3KCfkEXwCfKwyJFc,10899238
|
|
281
281
|
phoenix/server/static/assets/vendor-DxkFTwjz.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
|
|
282
|
-
phoenix/server/static/assets/vendor-arizeai
|
|
283
|
-
phoenix/server/static/assets/vendor-codemirror-
|
|
284
|
-
phoenix/server/static/assets/vendor-recharts-
|
|
282
|
+
phoenix/server/static/assets/vendor-arizeai--Q3ol330.js,sha256=DmwyVdLagWoGvOKRRAlwDibT1Qk4-IPmTDaLlXFjRFQ,308483
|
|
283
|
+
phoenix/server/static/assets/vendor-codemirror-B4bYvWa6.js,sha256=u5U1cT5PopN6AbYt4S55bI9X0REQ35Pa9BXZmb5Piww,392709
|
|
284
|
+
phoenix/server/static/assets/vendor-recharts-B4ZzJhNh.js,sha256=gS2dHFlC9A4tMk1tmNhtQo04SDcD2gQSkS4N6QS7d_0,282859
|
|
285
285
|
phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJBFR2RZO_xxeMXrqDp0AszZqHY,620972
|
|
286
286
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
287
287
|
phoenix/server/templates/index.html,sha256=ram6sfy2obf_F053ay35V30v-mnRWZ86rK-PstXLy1c,4457
|
|
@@ -299,7 +299,7 @@ phoenix/trace/fixtures.py,sha256=bwDD0rgIRdAi_9zwLLipke50oYuKJvZLvhiGsuvnGbs,191
|
|
|
299
299
|
phoenix/trace/otel.py,sha256=FZGzo0NPJqFBLkL_VmQeBne2z-LdHNPTz3oIZOuPPzQ,9997
|
|
300
300
|
phoenix/trace/projects.py,sha256=9dKv1aiKL4IYMFsg2xnC6EOIRO0YHtkR5o9ALHbMK9g,2178
|
|
301
301
|
phoenix/trace/schemas.py,sha256=0tghsJDaLKvSuxDDQGpUEQ0HkMsKmFWPO3lwJrJ9t84,5972
|
|
302
|
-
phoenix/trace/span_evaluations.py,sha256=
|
|
302
|
+
phoenix/trace/span_evaluations.py,sha256=x3nye9r2SQk1mrR3N05YbuWsgUKpMWwTRBtJTDBSj3Y,13156
|
|
303
303
|
phoenix/trace/span_json_decoder.py,sha256=J1_oDViuUoC4vxPg61U4EOZC1uEMcCzoj-kVjOFEE8k,3224
|
|
304
304
|
phoenix/trace/span_json_encoder.py,sha256=Wa7RvQTZSsFG4pdKSSFSO_4q4DjAOQuG8wLNJvYzsfM,2004
|
|
305
305
|
phoenix/trace/trace_dataset.py,sha256=7XyCe_7WemBmAiHw0kigmwkC1gPSLeVBqtrRe9FpM6s,14490
|
|
@@ -316,15 +316,15 @@ phoenix/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
316
316
|
phoenix/utilities/client.py,sha256=kr95EgpTJtCbb7CLoxC-KiyB2Wco03DZGIlcEmozHFs,5605
|
|
317
317
|
phoenix/utilities/deprecation.py,sha256=LL1LWfVqnYulceMQw2CggoIv8Uogrj7fAD_85devnoo,1067
|
|
318
318
|
phoenix/utilities/error_handling.py,sha256=5_ggfwmd0xZPoI3qi1fwm0x1F0i7g5j-gr0ny1TFzPU,1967
|
|
319
|
-
phoenix/utilities/json.py,sha256=
|
|
319
|
+
phoenix/utilities/json.py,sha256=UE8WHZU060NY_8ZKJ97gmtEC2mp60d8tJFXyRqIz51Y,3743
|
|
320
320
|
phoenix/utilities/logging.py,sha256=NKvX43jWITgSTtHGCi6l5xp8jTjtQ8FQ_irEi9ybOxg,502
|
|
321
321
|
phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,445
|
|
322
322
|
phoenix/utilities/re.py,sha256=x8Xbk-Wa6qDMAtUd_7JtZvKtrYEuMY-bchB0n163_5c,2006
|
|
323
323
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
324
|
-
phoenix/utilities/template_formatters.py,sha256=
|
|
325
|
-
arize_phoenix-5.
|
|
326
|
-
arize_phoenix-5.
|
|
327
|
-
arize_phoenix-5.
|
|
328
|
-
arize_phoenix-5.
|
|
329
|
-
arize_phoenix-5.
|
|
330
|
-
arize_phoenix-5.
|
|
324
|
+
phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
|
|
325
|
+
arize_phoenix-5.12.0.dist-info/METADATA,sha256=DTW4JHWstCwzlLmXkne9Ls3JG9uGXXpGjSNqqz2BfqI,22671
|
|
326
|
+
arize_phoenix-5.12.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
327
|
+
arize_phoenix-5.12.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
|
|
328
|
+
arize_phoenix-5.12.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
329
|
+
arize_phoenix-5.12.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
330
|
+
arize_phoenix-5.12.0.dist-info/RECORD,,
|
phoenix/config.py
CHANGED
|
@@ -97,6 +97,13 @@ ENV_PHOENIX_SERVER_INSTRUMENTATION_OTLP_TRACE_COLLECTOR_GRPC_ENDPOINT = (
|
|
|
97
97
|
ENV_PHOENIX_ENABLE_AUTH = "PHOENIX_ENABLE_AUTH"
|
|
98
98
|
ENV_PHOENIX_DISABLE_RATE_LIMIT = "PHOENIX_DISABLE_RATE_LIMIT"
|
|
99
99
|
ENV_PHOENIX_SECRET = "PHOENIX_SECRET"
|
|
100
|
+
ENV_PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD = "PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD"
|
|
101
|
+
"""
|
|
102
|
+
The initial password for the default admin account, which defaults to ‘admin’ if not
|
|
103
|
+
explicitly set. Note that changing this value will have no effect if the default admin
|
|
104
|
+
record already exists in the database. In such cases, the default admin password must
|
|
105
|
+
be updated manually in the application.
|
|
106
|
+
"""
|
|
100
107
|
ENV_PHOENIX_API_KEY = "PHOENIX_API_KEY"
|
|
101
108
|
ENV_PHOENIX_USE_SECURE_COOKIES = "PHOENIX_USE_SECURE_COOKIES"
|
|
102
109
|
ENV_PHOENIX_ACCESS_TOKEN_EXPIRY_MINUTES = "PHOENIX_ACCESS_TOKEN_EXPIRY_MINUTES"
|
|
@@ -274,6 +281,12 @@ def get_env_phoenix_secret() -> Optional[str]:
|
|
|
274
281
|
return phoenix_secret
|
|
275
282
|
|
|
276
283
|
|
|
284
|
+
def get_env_default_admin_initial_password() -> str:
|
|
285
|
+
from phoenix.auth import DEFAULT_ADMIN_PASSWORD
|
|
286
|
+
|
|
287
|
+
return os.environ.get(ENV_PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD) or DEFAULT_ADMIN_PASSWORD
|
|
288
|
+
|
|
289
|
+
|
|
277
290
|
def get_env_phoenix_use_secure_cookies() -> bool:
|
|
278
291
|
return _bool_val(ENV_PHOENIX_USE_SECURE_COOKIES, False)
|
|
279
292
|
|
phoenix/db/facilitator.py
CHANGED
|
@@ -13,13 +13,13 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
13
13
|
|
|
14
14
|
from phoenix.auth import (
|
|
15
15
|
DEFAULT_ADMIN_EMAIL,
|
|
16
|
-
DEFAULT_ADMIN_PASSWORD,
|
|
17
16
|
DEFAULT_ADMIN_USERNAME,
|
|
18
17
|
DEFAULT_SECRET_LENGTH,
|
|
19
18
|
DEFAULT_SYSTEM_EMAIL,
|
|
20
19
|
DEFAULT_SYSTEM_USERNAME,
|
|
21
20
|
compute_password_hash,
|
|
22
21
|
)
|
|
22
|
+
from phoenix.config import get_env_default_admin_initial_password
|
|
23
23
|
from phoenix.db import models
|
|
24
24
|
from phoenix.db.enums import COLUMN_ENUMS, UserRole
|
|
25
25
|
from phoenix.server.types import DbSessionFactory
|
|
@@ -97,7 +97,8 @@ async def _ensure_user_roles(session: AsyncSession) -> None:
|
|
|
97
97
|
admin_role_id := role_ids.get(admin_role)
|
|
98
98
|
) is not None:
|
|
99
99
|
salt = secrets.token_bytes(DEFAULT_SECRET_LENGTH)
|
|
100
|
-
|
|
100
|
+
password = get_env_default_admin_initial_password()
|
|
101
|
+
compute = partial(compute_password_hash, password=password, salt=salt)
|
|
101
102
|
loop = asyncio.get_running_loop()
|
|
102
103
|
hash_ = await loop.run_in_executor(None, compute)
|
|
103
104
|
admin_user = models.User(
|
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import importlib.util
|
|
3
3
|
import inspect
|
|
4
4
|
import json
|
|
5
|
+
import os
|
|
5
6
|
import time
|
|
6
7
|
from abc import ABC, abstractmethod
|
|
7
8
|
from collections.abc import AsyncIterator, Callable, Iterator
|
|
@@ -25,6 +26,7 @@ from phoenix.evals.models.rate_limiters import (
|
|
|
25
26
|
RateLimiter,
|
|
26
27
|
RateLimitError,
|
|
27
28
|
)
|
|
29
|
+
from phoenix.server.api.exceptions import BadRequest
|
|
28
30
|
from phoenix.server.api.helpers.playground_registry import PROVIDER_DEFAULT, register_llm_client
|
|
29
31
|
from phoenix.server.api.input_types.GenerativeModelInput import GenerativeModelInput
|
|
30
32
|
from phoenix.server.api.input_types.InvocationParameters import (
|
|
@@ -99,9 +101,9 @@ class PlaygroundRateLimiter(RateLimiter, KeyedSingleton):
|
|
|
99
101
|
super().__init__(
|
|
100
102
|
rate_limit_error=rate_limit_error,
|
|
101
103
|
max_rate_limit_retries=3,
|
|
102
|
-
initial_per_second_request_rate=
|
|
103
|
-
maximum_per_second_request_rate=
|
|
104
|
-
enforcement_window_minutes=
|
|
104
|
+
initial_per_second_request_rate=1.0,
|
|
105
|
+
maximum_per_second_request_rate=3.0,
|
|
106
|
+
enforcement_window_minutes=0.05,
|
|
105
107
|
rate_reduction_factor=0.5,
|
|
106
108
|
rate_increase_factor=0.01,
|
|
107
109
|
cooldown_seconds=5,
|
|
@@ -128,10 +130,11 @@ class PlaygroundRateLimiter(RateLimiter, KeyedSingleton):
|
|
|
128
130
|
self._rate_limit_handling.set() # Set the event as a failsafe
|
|
129
131
|
await self._throttler.async_wait_until_ready()
|
|
130
132
|
request_start_time = time.time()
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
maybe_coroutine = fn(*args, **kwargs)
|
|
134
|
+
if inspect.isawaitable(maybe_coroutine):
|
|
135
|
+
return await maybe_coroutine # type: ignore[no-any-return]
|
|
133
136
|
else:
|
|
134
|
-
return
|
|
137
|
+
return maybe_coroutine
|
|
135
138
|
except self._rate_limit_error:
|
|
136
139
|
async with self._rate_limit_handling_lock:
|
|
137
140
|
self._rate_limit_handling.clear() # prevent new requests from starting
|
|
@@ -141,10 +144,11 @@ class PlaygroundRateLimiter(RateLimiter, KeyedSingleton):
|
|
|
141
144
|
try:
|
|
142
145
|
request_start_time = time.time()
|
|
143
146
|
await self._throttler.async_wait_until_ready()
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
maybe_coroutine = fn(*args, **kwargs)
|
|
148
|
+
if inspect.isawaitable(maybe_coroutine):
|
|
149
|
+
return await maybe_coroutine # type: ignore[no-any-return]
|
|
146
150
|
else:
|
|
147
|
-
return
|
|
151
|
+
return maybe_coroutine
|
|
148
152
|
except self._rate_limit_error:
|
|
149
153
|
self._throttler.on_rate_limit_error(
|
|
150
154
|
request_start_time, verbose=self._verbose
|
|
@@ -258,6 +262,10 @@ class OpenAIStreamingClient(PlaygroundStreamingClient):
|
|
|
258
262
|
from openai import AsyncOpenAI
|
|
259
263
|
from openai import RateLimitError as OpenAIRateLimitError
|
|
260
264
|
|
|
265
|
+
# todo: check if custom base url is set before raising error to allow
|
|
266
|
+
# for custom endpoints that don't require an API key
|
|
267
|
+
if not (api_key := api_key or os.environ.get("OPENAI_API_KEY")):
|
|
268
|
+
raise BadRequest("An API key is required for OpenAI models")
|
|
261
269
|
super().__init__(model=model, api_key=api_key)
|
|
262
270
|
self._attributes[LLM_PROVIDER] = OpenInferenceLLMProviderValues.OPENAI.value
|
|
263
271
|
self._attributes[LLM_SYSTEM] = OpenInferenceLLMSystemValues.OPENAI.value
|
|
@@ -288,12 +296,14 @@ class OpenAIStreamingClient(PlaygroundStreamingClient):
|
|
|
288
296
|
BoundedFloatInvocationParameter(
|
|
289
297
|
invocation_name="frequency_penalty",
|
|
290
298
|
label="Frequency Penalty",
|
|
299
|
+
default_value=0.0,
|
|
291
300
|
min_value=-2.0,
|
|
292
301
|
max_value=2.0,
|
|
293
302
|
),
|
|
294
303
|
BoundedFloatInvocationParameter(
|
|
295
304
|
invocation_name="presence_penalty",
|
|
296
305
|
label="Presence Penalty",
|
|
306
|
+
default_value=0.0,
|
|
297
307
|
min_value=-2.0,
|
|
298
308
|
max_value=2.0,
|
|
299
309
|
),
|
|
@@ -306,12 +316,14 @@ class OpenAIStreamingClient(PlaygroundStreamingClient):
|
|
|
306
316
|
invocation_name="top_p",
|
|
307
317
|
canonical_name=CanonicalParameterName.TOP_P,
|
|
308
318
|
label="Top P",
|
|
319
|
+
default_value=1.0,
|
|
309
320
|
min_value=0.0,
|
|
310
321
|
max_value=1.0,
|
|
311
322
|
),
|
|
312
323
|
IntInvocationParameter(
|
|
313
324
|
invocation_name="seed",
|
|
314
325
|
canonical_name=CanonicalParameterName.RANDOM_SEED,
|
|
326
|
+
default_value=0,
|
|
315
327
|
label="Seed",
|
|
316
328
|
),
|
|
317
329
|
JSONInvocationParameter(
|
|
@@ -338,10 +350,14 @@ class OpenAIStreamingClient(PlaygroundStreamingClient):
|
|
|
338
350
|
from openai.types.chat import ChatCompletionStreamOptionsParam
|
|
339
351
|
|
|
340
352
|
# Convert standard messages to OpenAI messages
|
|
341
|
-
openai_messages = [
|
|
353
|
+
openai_messages = []
|
|
354
|
+
for message in messages:
|
|
355
|
+
openai_message = self.to_openai_chat_completion_param(*message)
|
|
356
|
+
if openai_message is not None:
|
|
357
|
+
openai_messages.append(openai_message)
|
|
342
358
|
tool_call_ids: dict[int, str] = {}
|
|
343
359
|
token_usage: Optional["CompletionUsage"] = None
|
|
344
|
-
throttled_create = self.rate_limiter.
|
|
360
|
+
throttled_create = self.rate_limiter._alimit(self.client.chat.completions.create)
|
|
345
361
|
async for chunk in await throttled_create(
|
|
346
362
|
messages=openai_messages,
|
|
347
363
|
model=self.model_name,
|
|
@@ -388,7 +404,7 @@ class OpenAIStreamingClient(PlaygroundStreamingClient):
|
|
|
388
404
|
content: JSONScalarType,
|
|
389
405
|
tool_call_id: Optional[str] = None,
|
|
390
406
|
tool_calls: Optional[list[JSONScalarType]] = None,
|
|
391
|
-
) -> "ChatCompletionMessageParam":
|
|
407
|
+
) -> Optional["ChatCompletionMessageParam"]:
|
|
392
408
|
from openai.types.chat import (
|
|
393
409
|
ChatCompletionAssistantMessageParam,
|
|
394
410
|
ChatCompletionSystemMessageParam,
|
|
@@ -480,6 +496,7 @@ class OpenAIO1StreamingClient(OpenAIStreamingClient):
|
|
|
480
496
|
invocation_name="seed",
|
|
481
497
|
canonical_name=CanonicalParameterName.RANDOM_SEED,
|
|
482
498
|
label="Seed",
|
|
499
|
+
default_value=0,
|
|
483
500
|
),
|
|
484
501
|
JSONInvocationParameter(
|
|
485
502
|
invocation_name="tool_choice",
|
|
@@ -488,65 +505,7 @@ class OpenAIO1StreamingClient(OpenAIStreamingClient):
|
|
|
488
505
|
),
|
|
489
506
|
]
|
|
490
507
|
|
|
491
|
-
|
|
492
|
-
self,
|
|
493
|
-
messages: list[
|
|
494
|
-
tuple[ChatCompletionMessageRole, str, Optional[str], Optional[list[JSONScalarType]]]
|
|
495
|
-
],
|
|
496
|
-
tools: list[JSONScalarType],
|
|
497
|
-
**invocation_parameters: Any,
|
|
498
|
-
) -> AsyncIterator[ChatCompletionChunk]:
|
|
499
|
-
from openai import NOT_GIVEN
|
|
500
|
-
|
|
501
|
-
# Convert standard messages to OpenAI messages
|
|
502
|
-
unfiltered_openai_messages = [
|
|
503
|
-
self.to_openai_o1_chat_completion_param(*message) for message in messages
|
|
504
|
-
]
|
|
505
|
-
|
|
506
|
-
# filter out unsupported messages
|
|
507
|
-
openai_messages: list[ChatCompletionMessageParam] = [
|
|
508
|
-
message for message in unfiltered_openai_messages if message is not None
|
|
509
|
-
]
|
|
510
|
-
|
|
511
|
-
tool_call_ids: dict[int, str] = {}
|
|
512
|
-
|
|
513
|
-
throttled_create = self.rate_limiter.alimit(self.client.chat.completions.create)
|
|
514
|
-
response = await throttled_create(
|
|
515
|
-
messages=openai_messages,
|
|
516
|
-
model=self.model_name,
|
|
517
|
-
tools=tools or NOT_GIVEN,
|
|
518
|
-
**invocation_parameters,
|
|
519
|
-
)
|
|
520
|
-
|
|
521
|
-
choice = response.choices[0]
|
|
522
|
-
message = choice.message
|
|
523
|
-
content = message.content
|
|
524
|
-
|
|
525
|
-
text_chunk = TextChunk(content=content)
|
|
526
|
-
yield text_chunk
|
|
527
|
-
|
|
528
|
-
if (tool_calls := message.tool_calls) is not None:
|
|
529
|
-
for tool_call_index, tool_call in enumerate(tool_calls):
|
|
530
|
-
tool_call_id = (
|
|
531
|
-
tool_call.id
|
|
532
|
-
if tool_call.id is not None
|
|
533
|
-
else tool_call_ids.get(tool_call_index, f"tool_call_{tool_call_index}")
|
|
534
|
-
)
|
|
535
|
-
tool_call_ids[tool_call_index] = tool_call_id
|
|
536
|
-
if (function := tool_call.function) is not None:
|
|
537
|
-
tool_call_chunk = ToolCallChunk(
|
|
538
|
-
id=tool_call_id,
|
|
539
|
-
function=FunctionCallChunk(
|
|
540
|
-
name=function.name or "",
|
|
541
|
-
arguments=function.arguments or "",
|
|
542
|
-
),
|
|
543
|
-
)
|
|
544
|
-
yield tool_call_chunk
|
|
545
|
-
|
|
546
|
-
if (usage := response.usage) is not None:
|
|
547
|
-
self._attributes.update(dict(self._llm_token_counts(usage)))
|
|
548
|
-
|
|
549
|
-
def to_openai_o1_chat_completion_param(
|
|
508
|
+
def to_openai_chat_completion_param(
|
|
550
509
|
self,
|
|
551
510
|
role: ChatCompletionMessageRole,
|
|
552
511
|
content: JSONScalarType,
|
|
@@ -618,12 +577,14 @@ class AzureOpenAIStreamingClient(OpenAIStreamingClient):
|
|
|
618
577
|
super().__init__(model=model, api_key=api_key)
|
|
619
578
|
self._attributes[LLM_PROVIDER] = OpenInferenceLLMProviderValues.AZURE.value
|
|
620
579
|
self._attributes[LLM_SYSTEM] = OpenInferenceLLMSystemValues.OPENAI.value
|
|
621
|
-
if model.endpoint
|
|
622
|
-
raise
|
|
580
|
+
if not (endpoint := model.endpoint or os.environ.get("AZURE_OPENAI_ENDPOINT")):
|
|
581
|
+
raise BadRequest("An Azure endpoint is required for Azure OpenAI models")
|
|
582
|
+
if not (api_version := model.api_version or os.environ.get("OPENAI_API_VERSION")):
|
|
583
|
+
raise BadRequest("An OpenAI API version is required for Azure OpenAI models")
|
|
623
584
|
self.client = AsyncAzureOpenAI(
|
|
624
585
|
api_key=api_key,
|
|
625
|
-
azure_endpoint=
|
|
626
|
-
api_version=
|
|
586
|
+
azure_endpoint=endpoint,
|
|
587
|
+
api_version=api_version,
|
|
627
588
|
)
|
|
628
589
|
|
|
629
590
|
|
|
@@ -631,8 +592,12 @@ class AzureOpenAIStreamingClient(OpenAIStreamingClient):
|
|
|
631
592
|
provider_key=GenerativeProviderKey.ANTHROPIC,
|
|
632
593
|
model_names=[
|
|
633
594
|
PROVIDER_DEFAULT,
|
|
595
|
+
"claude-3-5-sonnet-latest",
|
|
596
|
+
"claude-3-5-haiku-latest",
|
|
597
|
+
"claude-3-5-sonnet-20241022",
|
|
598
|
+
"claude-3-5-haiku-20241022",
|
|
634
599
|
"claude-3-5-sonnet-20240620",
|
|
635
|
-
"claude-3-opus-
|
|
600
|
+
"claude-3-opus-latest",
|
|
636
601
|
"claude-3-sonnet-20240229",
|
|
637
602
|
"claude-3-haiku-20240307",
|
|
638
603
|
],
|
|
@@ -648,6 +613,8 @@ class AnthropicStreamingClient(PlaygroundStreamingClient):
|
|
|
648
613
|
super().__init__(model=model, api_key=api_key)
|
|
649
614
|
self._attributes[LLM_PROVIDER] = OpenInferenceLLMProviderValues.ANTHROPIC.value
|
|
650
615
|
self._attributes[LLM_SYSTEM] = OpenInferenceLLMSystemValues.ANTHROPIC.value
|
|
616
|
+
if not (api_key := api_key or os.environ.get("ANTHROPIC_API_KEY")):
|
|
617
|
+
raise BadRequest("An API key is required for Anthropic models")
|
|
651
618
|
self.client = anthropic.AsyncAnthropic(api_key=api_key)
|
|
652
619
|
self.model_name = model.name
|
|
653
620
|
self.rate_limiter = PlaygroundRateLimiter(model.provider_key, anthropic.RateLimitError)
|
|
@@ -663,12 +630,14 @@ class AnthropicStreamingClient(PlaygroundStreamingClient):
|
|
|
663
630
|
invocation_name="max_tokens",
|
|
664
631
|
canonical_name=CanonicalParameterName.MAX_COMPLETION_TOKENS,
|
|
665
632
|
label="Max Tokens",
|
|
633
|
+
default_value=1024,
|
|
666
634
|
required=True,
|
|
667
635
|
),
|
|
668
636
|
BoundedFloatInvocationParameter(
|
|
669
637
|
invocation_name="temperature",
|
|
670
638
|
canonical_name=CanonicalParameterName.TEMPERATURE,
|
|
671
639
|
label="Temperature",
|
|
640
|
+
default_value=0.0,
|
|
672
641
|
min_value=0.0,
|
|
673
642
|
max_value=1.0,
|
|
674
643
|
),
|
|
@@ -681,6 +650,7 @@ class AnthropicStreamingClient(PlaygroundStreamingClient):
|
|
|
681
650
|
invocation_name="top_p",
|
|
682
651
|
canonical_name=CanonicalParameterName.TOP_P,
|
|
683
652
|
label="Top P",
|
|
653
|
+
default_value=1.0,
|
|
684
654
|
min_value=0.0,
|
|
685
655
|
max_value=1.0,
|
|
686
656
|
),
|
|
@@ -707,7 +677,6 @@ class AnthropicStreamingClient(PlaygroundStreamingClient):
|
|
|
707
677
|
"messages": anthropic_messages,
|
|
708
678
|
"model": self.model_name,
|
|
709
679
|
"system": system_prompt,
|
|
710
|
-
"max_tokens": 1024,
|
|
711
680
|
"tools": tools,
|
|
712
681
|
**invocation_parameters,
|
|
713
682
|
}
|
|
@@ -820,6 +789,12 @@ class GeminiStreamingClient(PlaygroundStreamingClient):
|
|
|
820
789
|
super().__init__(model=model, api_key=api_key)
|
|
821
790
|
self._attributes[LLM_PROVIDER] = OpenInferenceLLMProviderValues.GOOGLE.value
|
|
822
791
|
self._attributes[LLM_SYSTEM] = OpenInferenceLLMSystemValues.VERTEXAI.value
|
|
792
|
+
if not (
|
|
793
|
+
api_key := api_key
|
|
794
|
+
or os.environ.get("GEMINI_API_KEY")
|
|
795
|
+
or os.environ.get("GOOGLE_API_KEY")
|
|
796
|
+
):
|
|
797
|
+
raise BadRequest("An API key is required for Gemini models")
|
|
823
798
|
google_genai.configure(api_key=api_key)
|
|
824
799
|
self.model_name = model.name
|
|
825
800
|
|
|
@@ -851,21 +826,25 @@ class GeminiStreamingClient(PlaygroundStreamingClient):
|
|
|
851
826
|
FloatInvocationParameter(
|
|
852
827
|
invocation_name="presence_penalty",
|
|
853
828
|
label="Presence Penalty",
|
|
829
|
+
default_value=0.0,
|
|
854
830
|
),
|
|
855
831
|
FloatInvocationParameter(
|
|
856
832
|
invocation_name="frequency_penalty",
|
|
857
833
|
label="Frequency Penalty",
|
|
834
|
+
default_value=0.0,
|
|
858
835
|
),
|
|
859
836
|
BoundedFloatInvocationParameter(
|
|
860
837
|
invocation_name="top_p",
|
|
861
838
|
canonical_name=CanonicalParameterName.TOP_P,
|
|
862
839
|
label="Top P",
|
|
840
|
+
default_value=1.0,
|
|
863
841
|
min_value=0.0,
|
|
864
842
|
max_value=1.0,
|
|
865
843
|
),
|
|
866
844
|
BoundedFloatInvocationParameter(
|
|
867
845
|
invocation_name="top_k",
|
|
868
846
|
label="Top K",
|
|
847
|
+
default_value=1.0,
|
|
869
848
|
min_value=0.0,
|
|
870
849
|
max_value=1.0,
|
|
871
850
|
),
|
|
@@ -873,6 +852,7 @@ class GeminiStreamingClient(PlaygroundStreamingClient):
|
|
|
873
852
|
invocation_name="seed",
|
|
874
853
|
canonical_name=CanonicalParameterName.RANDOM_SEED,
|
|
875
854
|
label="Seed",
|
|
855
|
+
default_value=0,
|
|
876
856
|
),
|
|
877
857
|
]
|
|
878
858
|
|
|
@@ -907,6 +887,13 @@ class GeminiStreamingClient(PlaygroundStreamingClient):
|
|
|
907
887
|
chat = client.start_chat(history=gemini_message_history)
|
|
908
888
|
stream = await chat.send_message_async(**gemini_params)
|
|
909
889
|
async for event in stream:
|
|
890
|
+
self._attributes.update(
|
|
891
|
+
{
|
|
892
|
+
LLM_TOKEN_COUNT_PROMPT: event.usage_metadata.prompt_token_count,
|
|
893
|
+
LLM_TOKEN_COUNT_COMPLETION: event.usage_metadata.candidates_token_count,
|
|
894
|
+
LLM_TOKEN_COUNT_TOTAL: event.usage_metadata.total_token_count,
|
|
895
|
+
}
|
|
896
|
+
)
|
|
910
897
|
yield TextChunk(content=event.text)
|
|
911
898
|
|
|
912
899
|
def _build_gemini_messages(
|
|
@@ -379,6 +379,11 @@ def _llm_output_messages(
|
|
|
379
379
|
if content := "".join(chunk.content for chunk in text_chunks):
|
|
380
380
|
yield f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_CONTENT}", content
|
|
381
381
|
for tool_call_index, (_tool_call_id, tool_call_chunks_) in enumerate(tool_call_chunks.items()):
|
|
382
|
+
if _tool_call_id:
|
|
383
|
+
yield (
|
|
384
|
+
f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_TOOL_CALLS}.{tool_call_index}.{TOOL_CALL_ID}",
|
|
385
|
+
_tool_call_id,
|
|
386
|
+
)
|
|
382
387
|
if tool_call_chunks_ and (name := tool_call_chunks_[0].function.name):
|
|
383
388
|
yield (
|
|
384
389
|
f"{LLM_OUTPUT_MESSAGES}.0.{MESSAGE_TOOL_CALLS}.{tool_call_index}.{TOOL_CALL_FUNCTION_NAME}",
|
|
@@ -441,6 +446,7 @@ MESSAGE_CONTENT = MessageAttributes.MESSAGE_CONTENT
|
|
|
441
446
|
MESSAGE_ROLE = MessageAttributes.MESSAGE_ROLE
|
|
442
447
|
MESSAGE_TOOL_CALLS = MessageAttributes.MESSAGE_TOOL_CALLS
|
|
443
448
|
|
|
449
|
+
TOOL_CALL_ID = ToolCallAttributes.TOOL_CALL_ID
|
|
444
450
|
TOOL_CALL_FUNCTION_NAME = ToolCallAttributes.TOOL_CALL_FUNCTION_NAME
|
|
445
451
|
TOOL_CALL_FUNCTION_ARGUMENTS_JSON = ToolCallAttributes.TOOL_CALL_FUNCTION_ARGUMENTS_JSON
|
|
446
452
|
TOOL_CALL_ID = ToolCallAttributes.TOOL_CALL_ID
|