arize-phoenix 11.10.1__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.

Files changed (22) hide show
  1. {arize_phoenix-11.10.1.dist-info → arize_phoenix-11.11.0.dist-info}/METADATA +1 -1
  2. {arize_phoenix-11.10.1.dist-info → arize_phoenix-11.11.0.dist-info}/RECORD +20 -19
  3. phoenix/server/api/input_types/PromptFilter.py +14 -0
  4. phoenix/server/api/queries.py +14 -3
  5. phoenix/server/api/types/GenerativeModel.py +48 -3
  6. phoenix/server/api/types/Project.py +125 -0
  7. phoenix/server/static/.vite/manifest.json +44 -44
  8. phoenix/server/static/assets/{components-XAeml0-1.js → components-B7lK-RgC.js} +396 -314
  9. phoenix/server/static/assets/{index-D7EtHUpz.js → index-CpePoyTU.js} +11 -2
  10. phoenix/server/static/assets/{pages-CPfaxiKa.js → pages-CnVg_GUi.js} +518 -482
  11. phoenix/server/static/assets/{vendor-arizeai-4fVwwnrI.js → vendor-arizeai-CXCKGfvH.js} +3 -3
  12. phoenix/server/static/assets/vendor-codemirror-DWr46-WB.js +27 -0
  13. phoenix/server/static/assets/{vendor-recharts-w6bSawXG.js → vendor-recharts-0Yf6lanX.js} +1 -1
  14. phoenix/server/static/assets/{vendor-shiki-CplrhwOk.js → vendor-shiki-Caei6iKO.js} +1 -1
  15. phoenix/server/static/assets/vendor-uWG2jYEi.js +936 -0
  16. phoenix/version.py +1 -1
  17. phoenix/server/static/assets/vendor-DhvamIr8.js +0 -939
  18. phoenix/server/static/assets/vendor-codemirror-DRfFHb57.js +0 -33
  19. {arize_phoenix-11.10.1.dist-info → arize_phoenix-11.11.0.dist-info}/WHEEL +0 -0
  20. {arize_phoenix-11.10.1.dist-info → arize_phoenix-11.11.0.dist-info}/entry_points.txt +0 -0
  21. {arize_phoenix-11.10.1.dist-info → arize_phoenix-11.11.0.dist-info}/licenses/IP_NOTICE +0 -0
  22. {arize_phoenix-11.10.1.dist-info → arize_phoenix-11.11.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.10.1
3
+ Version: 11.11.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
@@ -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=xvvOboOAs4Htuc0_lAWkZ6Ru9ZUbUhsIFFXYCIMHAAE,24
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=fvbdyhJ57I6DPrNEBkWwhw8BRF7DyIZ3LtYwXwI7yVw,45651
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
@@ -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=cAZdZ0dgOwrbJqIInP2Iirk3CYhQWK8dGwLvKfJeJlI,5814
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=FE93mSrWKv1rcCPECfp1hsCen2012SPp-1pCEVuq5ic,64039
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=67Jr1Pbvt4QUZP4O98Fe_9tPLJvVHSKqSJeYcfC2oVk,2165
391
- phoenix/server/static/assets/components-XAeml0-1.js,sha256=aCeHz5UUtnMLqWDeVRMmHJnjMHgwEDjpdRoz_tJ063s,620501
392
- phoenix/server/static/assets/index-D7EtHUpz.js,sha256=FBp-_XLUtdcJ3wLAdu0peRKAPTkU2Ceu_U2Fu-QRfDg,62601
393
- phoenix/server/static/assets/pages-CPfaxiKa.js,sha256=EyoqjFXTFOgpww-GDBYnH7KfsLfV84OI06WYG0xmUjo,1190498
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-DhvamIr8.js,sha256=hZdQcaVySUPWh4GXRhuL-PhUy2K4sJLs6gYxde6qB_I,2748936
396
- phoenix/server/static/assets/vendor-arizeai-4fVwwnrI.js,sha256=8eBZfGf2fjjNoWDW31mHXdxkqSlWItGj7eHhbnH92Qk,151750
397
- phoenix/server/static/assets/vendor-codemirror-DRfFHb57.js,sha256=v-q8mq6f-EbjtvgzLTQYF-SNKX4R6LOGJe5ucOMqx1w,781264
398
- phoenix/server/static/assets/vendor-recharts-w6bSawXG.js,sha256=uTuxE0vslP7_y-F1dOU5K2nXke7qe8JMdE2SnRGCVKM,231651
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.10.1.dist-info/METADATA,sha256=wd_5sRdhTNXTvCYeob4xrZbZiPxZ81ENs-yF8PMEIyE,30851
441
- arize_phoenix-11.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
442
- arize_phoenix-11.10.1.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
443
- arize_phoenix-11.10.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
444
- arize_phoenix-11.10.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
445
- arize_phoenix-11.10.1.dist-info/RECORD,,
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,,
@@ -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
@@ -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, cast
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 = cast(Iterable[tuple[str, int]], await session.execute(stmt))
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 typing_extensions import assert_never
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(self, info: Info[Context, None]) -> SpanCostSummary:
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(model: models.GenerativeModel) -> GenerativeModel:
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)
@@ -33,6 +33,7 @@ from phoenix.server.api.types.AnnotationConfig import AnnotationConfig, to_gql_a
33
33
  from phoenix.server.api.types.AnnotationSummary import AnnotationSummary
34
34
  from phoenix.server.api.types.CostBreakdown import CostBreakdown
35
35
  from phoenix.server.api.types.DocumentEvaluationSummary import DocumentEvaluationSummary
36
+ from phoenix.server.api.types.GenerativeModel import GenerativeModel, to_gql_generative_model
36
37
  from phoenix.server.api.types.pagination import (
37
38
  ConnectionArgs,
38
39
  Cursor,
@@ -1326,6 +1327,130 @@ class Project(Node):
1326
1327
  names=sorted(list(unique_names)),
1327
1328
  )
1328
1329
 
1330
+ @strawberry.field
1331
+ async def top_models_by_cost(
1332
+ self,
1333
+ info: Info[Context, None],
1334
+ time_range: TimeRange,
1335
+ ) -> list[GenerativeModel]:
1336
+ if time_range.start is None:
1337
+ raise BadRequest("Start time is required")
1338
+
1339
+ async with info.context.db() as session:
1340
+ stmt = (
1341
+ select(
1342
+ models.GenerativeModel,
1343
+ func.sum(models.SpanCost.total_tokens).label("total_tokens"),
1344
+ func.sum(models.SpanCost.prompt_tokens).label("prompt_tokens"),
1345
+ func.sum(models.SpanCost.completion_tokens).label("completion_tokens"),
1346
+ func.sum(models.SpanCost.total_cost).label("total_cost"),
1347
+ func.sum(models.SpanCost.prompt_cost).label("prompt_cost"),
1348
+ func.sum(models.SpanCost.completion_cost).label("completion_cost"),
1349
+ )
1350
+ .join(
1351
+ models.SpanCost,
1352
+ models.SpanCost.model_id == models.GenerativeModel.id,
1353
+ )
1354
+ .join(
1355
+ models.Trace,
1356
+ models.SpanCost.trace_rowid == models.Trace.id,
1357
+ )
1358
+ .where(models.Trace.project_rowid == self.project_rowid)
1359
+ .where(models.SpanCost.model_id.isnot(None))
1360
+ .where(models.SpanCost.span_start_time >= time_range.start)
1361
+ .group_by(models.SpanCost.model_id)
1362
+ .order_by(func.sum(models.SpanCost.total_cost).desc())
1363
+ )
1364
+ if time_range.end:
1365
+ stmt = stmt.where(models.SpanCost.span_start_time < time_range.end)
1366
+ results: list[GenerativeModel] = []
1367
+ async for (
1368
+ model,
1369
+ total_tokens,
1370
+ prompt_tokens,
1371
+ completion_tokens,
1372
+ total_cost,
1373
+ prompt_cost,
1374
+ completion_cost,
1375
+ ) in await session.stream(stmt):
1376
+ cost_summary = SpanCostSummary(
1377
+ prompt=CostBreakdown(tokens=prompt_tokens, cost=prompt_cost),
1378
+ completion=CostBreakdown(tokens=completion_tokens, cost=completion_cost),
1379
+ total=CostBreakdown(tokens=total_tokens, cost=total_cost),
1380
+ )
1381
+ cache_time_range = TimeRange(
1382
+ start=time_range.start,
1383
+ end=time_range.end,
1384
+ )
1385
+ gql_model = to_gql_generative_model(model)
1386
+ gql_model.add_cached_cost_summary(
1387
+ self.project_rowid, cache_time_range, cost_summary
1388
+ )
1389
+ results.append(gql_model)
1390
+ return results
1391
+
1392
+ @strawberry.field
1393
+ async def top_models_by_token_count(
1394
+ self,
1395
+ info: Info[Context, None],
1396
+ time_range: TimeRange,
1397
+ ) -> list[GenerativeModel]:
1398
+ if time_range.start is None:
1399
+ raise BadRequest("Start time is required")
1400
+
1401
+ async with info.context.db() as session:
1402
+ stmt = (
1403
+ select(
1404
+ models.GenerativeModel,
1405
+ func.sum(models.SpanCost.total_tokens).label("total_tokens"),
1406
+ func.sum(models.SpanCost.prompt_tokens).label("prompt_tokens"),
1407
+ func.sum(models.SpanCost.completion_tokens).label("completion_tokens"),
1408
+ func.sum(models.SpanCost.total_cost).label("total_cost"),
1409
+ func.sum(models.SpanCost.prompt_cost).label("prompt_cost"),
1410
+ func.sum(models.SpanCost.completion_cost).label("completion_cost"),
1411
+ )
1412
+ .join(
1413
+ models.SpanCost,
1414
+ models.SpanCost.model_id == models.GenerativeModel.id,
1415
+ )
1416
+ .join(
1417
+ models.Trace,
1418
+ models.SpanCost.trace_rowid == models.Trace.id,
1419
+ )
1420
+ .where(models.Trace.project_rowid == self.project_rowid)
1421
+ .where(models.SpanCost.model_id.isnot(None))
1422
+ .where(models.SpanCost.span_start_time >= time_range.start)
1423
+ .group_by(models.SpanCost.model_id)
1424
+ .order_by(func.sum(models.SpanCost.total_tokens).desc())
1425
+ )
1426
+ if time_range.end:
1427
+ stmt = stmt.where(models.SpanCost.span_start_time < time_range.end)
1428
+ results: list[GenerativeModel] = []
1429
+ async for (
1430
+ model,
1431
+ total_tokens,
1432
+ prompt_tokens,
1433
+ completion_tokens,
1434
+ total_cost,
1435
+ prompt_cost,
1436
+ completion_cost,
1437
+ ) in await session.stream(stmt):
1438
+ cost_summary = SpanCostSummary(
1439
+ prompt=CostBreakdown(tokens=prompt_tokens, cost=prompt_cost),
1440
+ completion=CostBreakdown(tokens=completion_tokens, cost=completion_cost),
1441
+ total=CostBreakdown(tokens=total_tokens, cost=total_cost),
1442
+ )
1443
+ cache_time_range = TimeRange(
1444
+ start=time_range.start,
1445
+ end=time_range.end,
1446
+ )
1447
+ gql_model = to_gql_generative_model(model)
1448
+ gql_model.add_cached_cost_summary(
1449
+ self.project_rowid, cache_time_range, cost_summary
1450
+ )
1451
+ results.append(gql_model)
1452
+ return results
1453
+
1329
1454
 
1330
1455
  @strawberry.type
1331
1456
  class SpanCountTimeSeriesDataPoint:
@@ -1,87 +1,87 @@
1
1
  {
2
- "_components-XAeml0-1.js": {
3
- "file": "assets/components-XAeml0-1.js",
2
+ "_components-B7lK-RgC.js": {
3
+ "file": "assets/components-B7lK-RgC.js",
4
4
  "name": "components",
5
5
  "imports": [
6
- "_vendor-DhvamIr8.js",
7
- "_pages-CPfaxiKa.js",
8
- "_vendor-arizeai-4fVwwnrI.js",
9
- "_vendor-codemirror-DRfFHb57.js",
6
+ "_vendor-uWG2jYEi.js",
7
+ "_pages-CnVg_GUi.js",
8
+ "_vendor-arizeai-CXCKGfvH.js",
9
+ "_vendor-codemirror-DWr46-WB.js",
10
10
  "_vendor-three-C5WAXd5r.js"
11
11
  ]
12
12
  },
13
- "_pages-CPfaxiKa.js": {
14
- "file": "assets/pages-CPfaxiKa.js",
13
+ "_pages-CnVg_GUi.js": {
14
+ "file": "assets/pages-CnVg_GUi.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
- "_vendor-DhvamIr8.js",
18
- "_vendor-arizeai-4fVwwnrI.js",
19
- "_components-XAeml0-1.js",
20
- "_vendor-codemirror-DRfFHb57.js",
21
- "_vendor-recharts-w6bSawXG.js"
17
+ "_vendor-uWG2jYEi.js",
18
+ "_vendor-arizeai-CXCKGfvH.js",
19
+ "_components-B7lK-RgC.js",
20
+ "_vendor-codemirror-DWr46-WB.js",
21
+ "_vendor-recharts-0Yf6lanX.js"
22
22
  ]
23
23
  },
24
24
  "_vendor-CqDb5u4o.css": {
25
25
  "file": "assets/vendor-CqDb5u4o.css",
26
26
  "src": "_vendor-CqDb5u4o.css"
27
27
  },
28
- "_vendor-DhvamIr8.js": {
29
- "file": "assets/vendor-DhvamIr8.js",
30
- "name": "vendor",
31
- "imports": [
32
- "_vendor-three-C5WAXd5r.js"
33
- ],
34
- "css": [
35
- "assets/vendor-CqDb5u4o.css"
36
- ]
37
- },
38
- "_vendor-arizeai-4fVwwnrI.js": {
39
- "file": "assets/vendor-arizeai-4fVwwnrI.js",
28
+ "_vendor-arizeai-CXCKGfvH.js": {
29
+ "file": "assets/vendor-arizeai-CXCKGfvH.js",
40
30
  "name": "vendor-arizeai",
41
31
  "imports": [
42
- "_vendor-DhvamIr8.js"
32
+ "_vendor-uWG2jYEi.js"
43
33
  ]
44
34
  },
45
- "_vendor-codemirror-DRfFHb57.js": {
46
- "file": "assets/vendor-codemirror-DRfFHb57.js",
35
+ "_vendor-codemirror-DWr46-WB.js": {
36
+ "file": "assets/vendor-codemirror-DWr46-WB.js",
47
37
  "name": "vendor-codemirror",
48
38
  "imports": [
49
- "_vendor-DhvamIr8.js",
50
- "_vendor-shiki-CplrhwOk.js"
39
+ "_vendor-uWG2jYEi.js",
40
+ "_vendor-shiki-Caei6iKO.js"
51
41
  ]
52
42
  },
53
- "_vendor-recharts-w6bSawXG.js": {
54
- "file": "assets/vendor-recharts-w6bSawXG.js",
43
+ "_vendor-recharts-0Yf6lanX.js": {
44
+ "file": "assets/vendor-recharts-0Yf6lanX.js",
55
45
  "name": "vendor-recharts",
56
46
  "imports": [
57
- "_vendor-DhvamIr8.js"
47
+ "_vendor-uWG2jYEi.js"
58
48
  ]
59
49
  },
60
- "_vendor-shiki-CplrhwOk.js": {
61
- "file": "assets/vendor-shiki-CplrhwOk.js",
50
+ "_vendor-shiki-Caei6iKO.js": {
51
+ "file": "assets/vendor-shiki-Caei6iKO.js",
62
52
  "name": "vendor-shiki",
63
53
  "imports": [
64
- "_vendor-DhvamIr8.js"
54
+ "_vendor-uWG2jYEi.js"
65
55
  ]
66
56
  },
67
57
  "_vendor-three-C5WAXd5r.js": {
68
58
  "file": "assets/vendor-three-C5WAXd5r.js",
69
59
  "name": "vendor-three"
70
60
  },
61
+ "_vendor-uWG2jYEi.js": {
62
+ "file": "assets/vendor-uWG2jYEi.js",
63
+ "name": "vendor",
64
+ "imports": [
65
+ "_vendor-three-C5WAXd5r.js"
66
+ ],
67
+ "css": [
68
+ "assets/vendor-CqDb5u4o.css"
69
+ ]
70
+ },
71
71
  "index.tsx": {
72
- "file": "assets/index-D7EtHUpz.js",
72
+ "file": "assets/index-CpePoyTU.js",
73
73
  "name": "index",
74
74
  "src": "index.tsx",
75
75
  "isEntry": true,
76
76
  "imports": [
77
- "_vendor-DhvamIr8.js",
78
- "_vendor-arizeai-4fVwwnrI.js",
79
- "_pages-CPfaxiKa.js",
80
- "_components-XAeml0-1.js",
77
+ "_vendor-uWG2jYEi.js",
78
+ "_vendor-arizeai-CXCKGfvH.js",
79
+ "_pages-CnVg_GUi.js",
80
+ "_components-B7lK-RgC.js",
81
81
  "_vendor-three-C5WAXd5r.js",
82
- "_vendor-codemirror-DRfFHb57.js",
83
- "_vendor-shiki-CplrhwOk.js",
84
- "_vendor-recharts-w6bSawXG.js"
82
+ "_vendor-codemirror-DWr46-WB.js",
83
+ "_vendor-shiki-Caei6iKO.js",
84
+ "_vendor-recharts-0Yf6lanX.js"
85
85
  ]
86
86
  }
87
87
  }