arize-phoenix 4.20.1__py3-none-any.whl → 4.21.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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arize-phoenix
3
- Version: 4.20.1
3
+ Version: 4.21.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
@@ -5,7 +5,7 @@ phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
5
5
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
6
  phoenix/services.py,sha256=aTxhcOA1pZHB6U-B3TEcp6fqDF5oT0xCUvEUNMZVTUQ,5175
7
7
  phoenix/settings.py,sha256=cO-qgis_S27nHirTobYI9hHPfZH18R--WMmxNdsVUwc,273
8
- phoenix/version.py,sha256=x2GXndd_stTekM40AdALx0Eq-Nbip56uWQ4qvw-qCgE,23
8
+ phoenix/version.py,sha256=Nuzask72HqOtOnJvPTcGSrVdJM7YOawRe8WLFLDLIJ4,23
9
9
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
11
11
  phoenix/core/model.py,sha256=km_a--PBHOuA337ClRw9xqhOHhrUT6Rl9pz_zV0JYkQ,4843
@@ -18,23 +18,23 @@ phoenix/db/bulk_inserter.py,sha256=qgg8pt5k4VnHKOE0-KoReXVAfXRhLt-sMZihI-b4X9I,1
18
18
  phoenix/db/engines.py,sha256=R3btYTSOSd6BwRA59EmhhojL0HCQ7NnzFIXQrPYS0iU,4812
19
19
  phoenix/db/helpers.py,sha256=2zSc4n5IJfu-CaOFoBfqTB35M1nTFcAc8tqLsNtF2Jw,3488
20
20
  phoenix/db/migrate.py,sha256=MuhtNWnR24riROvarvKfbRb4_D5xuQi6P760vBUKl1E,2270
21
- phoenix/db/models.py,sha256=7DBWbxY3cx3ve2P1I0kkDKXzlt04zEFJuRPJWsVpH-I,20422
21
+ phoenix/db/models.py,sha256=1fSwI9NjXcVv7PT40VEeKGGI13TWbqzrYfIemXV-DYY,20837
22
22
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
24
24
  phoenix/db/insertion/dataset.py,sha256=_vxy5e6W5jEuvO2fMKbbNCn9JvHkwI4LRKk_10eKFVg,7171
25
25
  phoenix/db/insertion/document_annotation.py,sha256=qp8E63LzlthAScQg6opqln5Qg1d7UdtP3rkYL4riTgU,5983
26
26
  phoenix/db/insertion/evaluation.py,sha256=SoI85N3MYUSeNgjKa5WzFw14OfNjNTjExv-2m3sxaR8,6371
27
27
  phoenix/db/insertion/helpers.py,sha256=z_Wnckhdf-F7xadWgaAV5ScXnLft8EtaYJCuIkma2Vw,3486
28
- phoenix/db/insertion/span.py,sha256=T9jOW3lyWte-JGD7wlP2ZqtO0-V57d8z6U_TldnuGuk,5527
28
+ phoenix/db/insertion/span.py,sha256=-CXn2LlEv2u7xbz7m8X5jALQ-BUNNzTxPJEzSmjhTpA,5958
29
29
  phoenix/db/insertion/span_annotation.py,sha256=eQK7fFjKjZGj25Qf_PTU9Q8DZiYJAw4lfcfdLKFsKe0,5259
30
30
  phoenix/db/insertion/trace_annotation.py,sha256=36CwcxSvDo2r-_y_GlmMXGnlH4BKVaMu5BWM9w-9l7A,5324
31
31
  phoenix/db/insertion/types.py,sha256=nQYYnpzcPxj2kdUoXfKE8ilOKlx1zpKLPc40OGuBlfk,8149
32
32
  phoenix/db/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  phoenix/db/migrations/env.py,sha256=QbzB5zrRs6XQQmrYeUpuzeilcMlM-MsbaAgHHYcIHTI,3626
34
34
  phoenix/db/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
35
- phoenix/db/migrations/types.py,sha256=Frq1AKSyBKQQ0FLzON-EmgTqE4kNkOpHMsbWnI-WgCE,605
36
- phoenix/db/migrations/versions/10460e46d750_datasets.py,sha256=l69yZfScFrjfZZpY0gnqwhsDUEctLeo02qMgA_aOGDg,8155
37
- phoenix/db/migrations/versions/cf03bd6bae1d_init.py,sha256=CbWT3ZTR0CZqeT3zWLoTWhboFmnOy3Ju1z6Ztpq8WIM,8122
35
+ phoenix/db/migrations/versions/10460e46d750_datasets.py,sha256=eZAyz720DmpOd7RnuxDN2dVNXVuMrdlCA7eAOxyMtfs,8695
36
+ phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py,sha256=x6oKFwn7Zmite4G0trDQPpMCn0I7jejuBcN3-ivEuDg,3938
37
+ phoenix/db/migrations/versions/cf03bd6bae1d_init.py,sha256=09cpofqje8zi4eQFfUn-i21x7VcsUYOfLKKUlrtKrGc,8662
38
38
  phoenix/experiments/__init__.py,sha256=6JGwgUd7xCbGpuHqYZlsmErmYvVgv7N_j43bn3dUqsk,123
39
39
  phoenix/experiments/functions.py,sha256=4XaOLE1Co9sW_yjM1sypQClmOLtt9kwoxmhIEJ3f_rk,32209
40
40
  phoenix/experiments/tracing.py,sha256=wVpt8Ie9WNPoi1djJdcrkwCokHdTO0bicXViLg3O-1Y,2831
@@ -102,7 +102,7 @@ phoenix/server/api/dataloaders/span_dataset_examples.py,sha256=BtLZp11fyyeaWGGBP
102
102
  phoenix/server/api/dataloaders/span_descendants.py,sha256=b7jGTn0Hi22gv2yskloLnf3BG3upS9z5hnKLMT9Sxac,2094
103
103
  phoenix/server/api/dataloaders/span_evaluations.py,sha256=IfwXW23GQaWti8F49wSJocWf7Tklf2ZJ0F6aB4cSVHs,1248
104
104
  phoenix/server/api/dataloaders/span_projects.py,sha256=LbQWiboCFqq4CHS18OzvRUwL9yORqP26fh5p7JbpFdg,1244
105
- phoenix/server/api/dataloaders/token_counts.py,sha256=riJC3jGRJv2eyenk8Gkx7xwDaulb9SWla58wfcXZeCE,4947
105
+ phoenix/server/api/dataloaders/token_counts.py,sha256=6gDVely8BYiCBdmiq1ECO0lMChUYPIlsZbB34rmL1xM,4684
106
106
  phoenix/server/api/dataloaders/trace_evaluations.py,sha256=vraPehNsausR4dbdvq-HudRVNARJUbep3T-Ud9jwWYY,1262
107
107
  phoenix/server/api/dataloaders/trace_row_ids.py,sha256=RODX4NULlBzMxHMrsq0dp1ij6ZlLH4ZzQBnafGdxOvU,1100
108
108
  phoenix/server/api/dataloaders/cache/__init__.py,sha256=SYoOM9n8FJaMdQarma5d1blu-jIg2GB8Shqg5ezSzZ8,106
@@ -133,7 +133,7 @@ phoenix/server/api/input_types/PatchDatasetExamplesInput.py,sha256=E86aBGXDBC83j
133
133
  phoenix/server/api/input_types/PatchDatasetInput.py,sha256=OURtTVY8Z_oFEDtKwT1LCMaOK5D4QYo5TVQ6mDrex-g,328
134
134
  phoenix/server/api/input_types/PerformanceMetricInput.py,sha256=fElsLTSEYYgGFGMYTEGcYid39tXUKFdV_JkdHavMcbA,591
135
135
  phoenix/server/api/input_types/SpanAnnotationSort.py,sha256=T5pAGzmh4MiJp9JMAzNDByFVTczfw02FH4WFWwFezyI,361
136
- phoenix/server/api/input_types/SpanSort.py,sha256=BqV0DjI4m3RKFbzsHN47XNhJfFmErFIYg64c8EJAVMo,6312
136
+ phoenix/server/api/input_types/SpanSort.py,sha256=Dhvl8BIoV52yHoqntfOax_gUc15uH8ITI_00Ha7PvYc,5959
137
137
  phoenix/server/api/input_types/TimeRange.py,sha256=yzx-gxj8mDeGLft1FzU_x1MVEgIG5Pt6-f8PUVDgipQ,522
138
138
  phoenix/server/api/input_types/TraceAnnotationSort.py,sha256=BzwiUnMh2VsgQYnhDlbJ6ljHugqIS4YDUlYzvq_tl3o,365
139
139
  phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -205,7 +205,7 @@ phoenix/server/api/types/Retrieval.py,sha256=OhMK2ncjoyp5h1yjKhjlKpoTbQrMHuxmgSF
205
205
  phoenix/server/api/types/ScalarDriftMetricEnum.py,sha256=IUAcRPpgL41WdoIgK6cNk2Te38SspXGyEs-S1fY23_A,232
206
206
  phoenix/server/api/types/Segments.py,sha256=m2yoegrxA1Tn7ZAy1rMjjD1isc752MaAXMoffkBlvrM,2921
207
207
  phoenix/server/api/types/SortDir.py,sha256=OUpXhlCzCxPoXSDkJJygEs9Rw9pMymfaZUG5zPTrw4Y,152
208
- phoenix/server/api/types/Span.py,sha256=k3j8P06927jNK5P8VrqQLRhOu3pyiPlV5wdiGdXC1cE,15263
208
+ phoenix/server/api/types/Span.py,sha256=xzJoRuzFf1S64jDuPmGLjSFZ4oPyKUyfK1CtmtZ4LY0,14801
209
209
  phoenix/server/api/types/SpanAnnotation.py,sha256=6b5G-b_OoRvDL2ayWk7MkbqarLK-F-pQMx21CpUuNGY,1168
210
210
  phoenix/server/api/types/TimeSeries.py,sha256=wjzuxHFqCey0O7Ys25qiXyuqXK8an-osyNWUE8A_8G4,5227
211
211
  phoenix/server/api/types/Trace.py,sha256=-nh3A-S_BlQK1VSSOTWqM85l-WwJsRHifxeDi0sFWZE,3246
@@ -227,13 +227,13 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
227
227
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
228
228
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
229
229
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
230
- phoenix/server/static/.vite/manifest.json,sha256=iHg8InkOETcckHJQlUfToMmMPaocrvCM68tSU6BEU1Q,1929
231
- phoenix/server/static/assets/components-CAummAJx.js,sha256=LvIzotUMWSCvu2IT9QHFGK_JJB_LYXyFJagHFyu6AVI,184229
232
- phoenix/server/static/assets/index-Cg5hdf3g.js,sha256=-mDNUU79jpnfsiTaJztdQ_WVKySUcyRbbZyRCztZKJI,7362
233
- phoenix/server/static/assets/pages-BU__X1UX.js,sha256=tNQZgWWC9ycS2CMKUm5qdRlnhpPfIECvuM8E5AUOAtY,445374
230
+ phoenix/server/static/.vite/manifest.json,sha256=uUMHsl_PTGtJlYZ2ZOLRLq5uyja6vwRbryBq4ZtKYoU,1929
231
+ phoenix/server/static/assets/components-D2V-mOGq.js,sha256=A9VauVKZBYNd3FYG79nBexMNDWFPhn0uYLxv3MaoZjY,186964
232
+ phoenix/server/static/assets/index-B52Z3aZG.js,sha256=s5weFibNVveQw5Mv6qkwTPW63m3JEqbtcrCno_Z7qEI,7362
233
+ phoenix/server/static/assets/pages-CChOjmat.js,sha256=tZB8gxmm_B06n8rccl9a8U8JdC8whimsxlYWB8pFWvA,450347
234
234
  phoenix/server/static/assets/vendor-BMWfu6zp.js,sha256=AAVTM5SjGUI_CmAWFUFmhpp5VDhvCD-MrEoh-pXXADY,1355423
235
235
  phoenix/server/static/assets/vendor-DxkFTwjz.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
236
- phoenix/server/static/assets/vendor-arizeai-CkyzG9Wl.js,sha256=m1xvoung59IMplKeV-0XmsSPRwu1AJDRxwHbq6BN-oc,296355
236
+ phoenix/server/static/assets/vendor-arizeai-Sj74jm5V.js,sha256=9lD4YeMt5WtyfrqIApcH9WFQxyJJUtth0syWabkzX-I,304008
237
237
  phoenix/server/static/assets/vendor-codemirror-DO3VqEcD.js,sha256=M7t6xd6WpgKes25OOeGyxT1MU1dDrEKdmUBHgy5zslw,503031
238
238
  phoenix/server/static/assets/vendor-recharts-BGN0SxgJ.js,sha256=L9LAYSjuf0GHh1_PQh9bF4l9euWCDVQcnQN1RgMDMBw,282859
239
239
  phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJBFR2RZO_xxeMXrqDp0AszZqHY,620972
@@ -281,8 +281,8 @@ phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,
281
281
  phoenix/utilities/project.py,sha256=8IJuMM4yUMoooPi37sictGj8Etu9rGmq6RFtc9848cQ,436
282
282
  phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
283
283
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
284
- arize_phoenix-4.20.1.dist-info/METADATA,sha256=m65-hbRGN_Pg_ajtvbWhoKoGdqvq4Y8o3_850jJTGKM,11902
285
- arize_phoenix-4.20.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
286
- arize_phoenix-4.20.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
287
- arize_phoenix-4.20.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
288
- arize_phoenix-4.20.1.dist-info/RECORD,,
284
+ arize_phoenix-4.21.0.dist-info/METADATA,sha256=9epvC-jCcVrQ7I-bwHNr7dmEtOUvNCB_zjvVQhTe7NU,11902
285
+ arize_phoenix-4.21.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
286
+ arize_phoenix-4.21.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
287
+ arize_phoenix-4.21.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
288
+ arize_phoenix-4.21.0.dist-info/RECORD,,
@@ -71,6 +71,13 @@ async def insert_span(
71
71
  cumulative_llm_token_count_completion = cast(
72
72
  int, get_attribute_value(span.attributes, SpanAttributes.LLM_TOKEN_COUNT_COMPLETION) or 0
73
73
  )
74
+ llm_token_count_prompt = cast(
75
+ Optional[int], get_attribute_value(span.attributes, SpanAttributes.LLM_TOKEN_COUNT_PROMPT)
76
+ )
77
+ llm_token_count_completion = cast(
78
+ Optional[int],
79
+ get_attribute_value(span.attributes, SpanAttributes.LLM_TOKEN_COUNT_COMPLETION),
80
+ )
74
81
  if accumulation := (
75
82
  await session.execute(
76
83
  select(
@@ -100,6 +107,8 @@ async def insert_span(
100
107
  cumulative_error_count=cumulative_error_count,
101
108
  cumulative_llm_token_count_prompt=cumulative_llm_token_count_prompt,
102
109
  cumulative_llm_token_count_completion=cumulative_llm_token_count_completion,
110
+ llm_token_count_prompt=llm_token_count_prompt,
111
+ llm_token_count_completion=llm_token_count_completion,
103
112
  ),
104
113
  dialect=dialect,
105
114
  table=models.Span,
@@ -6,11 +6,37 @@ Create Date: 2024-05-10 11:24:23.985834
6
6
 
7
7
  """
8
8
 
9
- from typing import Sequence, Union
9
+ from typing import Any, Sequence, Union
10
10
 
11
11
  import sqlalchemy as sa
12
12
  from alembic import op
13
- from phoenix.db.migrations.types import JSON_
13
+ from sqlalchemy import JSON
14
+ from sqlalchemy.dialects import postgresql
15
+ from sqlalchemy.ext.compiler import compiles
16
+
17
+
18
+ class JSONB(JSON):
19
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
20
+ __visit_name__ = "JSONB"
21
+
22
+
23
+ @compiles(JSONB, "sqlite") # type: ignore
24
+ def _(*args: Any, **kwargs: Any) -> str:
25
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
26
+ return "JSONB"
27
+
28
+
29
+ JSON_ = (
30
+ JSON()
31
+ .with_variant(
32
+ postgresql.JSONB(), # type: ignore
33
+ "postgresql",
34
+ )
35
+ .with_variant(
36
+ JSONB(),
37
+ "sqlite",
38
+ )
39
+ )
14
40
 
15
41
  # revision identifiers, used by Alembic.
16
42
  revision: str = "10460e46d750"
@@ -0,0 +1,134 @@
1
+ """add token columns to spans table
2
+
3
+ Revision ID: 3be8647b87d8
4
+ Revises: 10460e46d750
5
+ Create Date: 2024-08-03 22:11:28.733133
6
+
7
+ """
8
+
9
+ from typing import Any, Dict, List, Optional, Sequence, TypedDict, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+ from openinference.semconv.trace import SpanAttributes
14
+ from sqlalchemy import (
15
+ JSON,
16
+ Dialect,
17
+ MetaData,
18
+ TypeDecorator,
19
+ update,
20
+ )
21
+ from sqlalchemy.dialects import postgresql
22
+ from sqlalchemy.ext.asyncio.engine import AsyncConnection
23
+ from sqlalchemy.ext.compiler import compiles
24
+ from sqlalchemy.orm import (
25
+ DeclarativeBase,
26
+ Mapped,
27
+ mapped_column,
28
+ )
29
+
30
+
31
+ class JSONB(JSON):
32
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
33
+ __visit_name__ = "JSONB"
34
+
35
+
36
+ @compiles(JSONB, "sqlite") # type: ignore
37
+ def _(*args: Any, **kwargs: Any) -> str:
38
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
39
+ return "JSONB"
40
+
41
+
42
+ JSON_ = (
43
+ JSON()
44
+ .with_variant(
45
+ postgresql.JSONB(), # type: ignore
46
+ "postgresql",
47
+ )
48
+ .with_variant(
49
+ JSONB(),
50
+ "sqlite",
51
+ )
52
+ )
53
+
54
+
55
+ class JsonDict(TypeDecorator[Dict[str, Any]]):
56
+ # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
57
+ cache_ok = True
58
+ impl = JSON_
59
+
60
+ def process_bind_param(self, value: Optional[Dict[str, Any]], _: Dialect) -> Dict[str, Any]:
61
+ return value if isinstance(value, dict) else {}
62
+
63
+
64
+ class JsonList(TypeDecorator[List[Any]]):
65
+ # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
66
+ cache_ok = True
67
+ impl = JSON_
68
+
69
+ def process_bind_param(self, value: Optional[List[Any]], _: Dialect) -> List[Any]:
70
+ return value if isinstance(value, list) else []
71
+
72
+
73
+ class ExperimentRunOutput(TypedDict, total=False):
74
+ task_output: Any
75
+
76
+
77
+ class Base(DeclarativeBase):
78
+ # Enforce best practices for naming constraints
79
+ # https://alembic.sqlalchemy.org/en/latest/naming.html#integration-of-naming-conventions-into-operations-autogenerate
80
+ metadata = MetaData(
81
+ naming_convention={
82
+ "ix": "ix_%(table_name)s_%(column_0_N_name)s",
83
+ "uq": "uq_%(table_name)s_%(column_0_N_name)s",
84
+ "ck": "ck_%(table_name)s_`%(constraint_name)s`",
85
+ "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
86
+ "pk": "pk_%(table_name)s",
87
+ }
88
+ )
89
+ type_annotation_map = {
90
+ Dict[str, Any]: JsonDict,
91
+ List[Dict[str, Any]]: JsonList,
92
+ ExperimentRunOutput: JsonDict,
93
+ }
94
+
95
+
96
+ class Span(Base):
97
+ __tablename__ = "spans"
98
+ id: Mapped[int] = mapped_column(primary_key=True)
99
+ attributes: Mapped[Dict[str, Any]]
100
+ llm_token_count_prompt: Mapped[Optional[int]]
101
+ llm_token_count_completion: Mapped[Optional[int]]
102
+
103
+
104
+ # revision identifiers, used by Alembic.
105
+ revision: str = "3be8647b87d8"
106
+ down_revision: Union[str, None] = "10460e46d750"
107
+ branch_labels: Union[str, Sequence[str], None] = None
108
+ depends_on: Union[str, Sequence[str], None] = None
109
+
110
+ LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT.split(".")
111
+ LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION.split(".")
112
+
113
+
114
+ async def get_token_counts_from_attributes(connection: AsyncConnection) -> None:
115
+ """
116
+ Gets token counts from attributes if present.
117
+ """
118
+ await connection.execute(
119
+ update(Span).values(
120
+ llm_token_count_prompt=Span.attributes[LLM_TOKEN_COUNT_PROMPT].as_float(),
121
+ llm_token_count_completion=Span.attributes[LLM_TOKEN_COUNT_COMPLETION].as_float(),
122
+ )
123
+ )
124
+
125
+
126
+ def upgrade() -> None:
127
+ op.add_column("spans", sa.Column("llm_token_count_prompt", sa.Integer, nullable=True))
128
+ op.add_column("spans", sa.Column("llm_token_count_completion", sa.Integer, nullable=True))
129
+ op.run_async(get_token_counts_from_attributes)
130
+
131
+
132
+ def downgrade() -> None:
133
+ op.drop_column("spans", "llm_token_count_completion")
134
+ op.drop_column("spans", "llm_token_count_prompt")
@@ -6,11 +6,37 @@ Create Date: 2024-04-03 19:41:48.871555
6
6
 
7
7
  """
8
8
 
9
- from typing import Sequence, Union
9
+ from typing import Any, Sequence, Union
10
10
 
11
11
  import sqlalchemy as sa
12
12
  from alembic import op
13
- from phoenix.db.migrations.types import JSON_
13
+ from sqlalchemy import JSON
14
+ from sqlalchemy.dialects import postgresql
15
+ from sqlalchemy.ext.compiler import compiles
16
+
17
+
18
+ class JSONB(JSON):
19
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
20
+ __visit_name__ = "JSONB"
21
+
22
+
23
+ @compiles(JSONB, "sqlite") # type: ignore
24
+ def _(*args: Any, **kwargs: Any) -> str:
25
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
26
+ return "JSONB"
27
+
28
+
29
+ JSON_ = (
30
+ JSON()
31
+ .with_variant(
32
+ postgresql.JSONB(), # type: ignore
33
+ "postgresql",
34
+ )
35
+ .with_variant(
36
+ JSONB(),
37
+ "sqlite",
38
+ )
39
+ )
14
40
 
15
41
  # revision identifiers, used by Alembic.
16
42
  revision: str = "cf03bd6bae1d"
phoenix/db/models.py CHANGED
@@ -197,7 +197,7 @@ class Span(Base):
197
197
  ForeignKey("traces.id", ondelete="CASCADE"),
198
198
  index=True,
199
199
  )
200
- span_id: Mapped[str]
200
+ span_id: Mapped[str] = mapped_column(index=True)
201
201
  parent_id: Mapped[Optional[str]] = mapped_column(index=True)
202
202
  name: Mapped[str]
203
203
  span_kind: Mapped[str]
@@ -214,6 +214,8 @@ class Span(Base):
214
214
  cumulative_error_count: Mapped[int]
215
215
  cumulative_llm_token_count_prompt: Mapped[int]
216
216
  cumulative_llm_token_count_completion: Mapped[int]
217
+ llm_token_count_prompt: Mapped[Optional[int]]
218
+ llm_token_count_completion: Mapped[Optional[int]]
217
219
 
218
220
  @hybrid_property
219
221
  def latency_ms(self) -> float:
@@ -230,6 +232,12 @@ class Span(Base):
230
232
  def cumulative_llm_token_count_total(self) -> int:
231
233
  return self.cumulative_llm_token_count_prompt + self.cumulative_llm_token_count_completion
232
234
 
235
+ @hybrid_property
236
+ def llm_token_count_total(self) -> Optional[int]:
237
+ if self.llm_token_count_prompt is None and self.llm_token_count_completion is None:
238
+ return None
239
+ return (self.llm_token_count_prompt or 0) + (self.llm_token_count_completion or 0)
240
+
233
241
  trace: Mapped["Trace"] = relationship("Trace", back_populates="spans")
234
242
  document_annotations: Mapped[List["DocumentAnnotation"]] = relationship(back_populates="span")
235
243
  dataset_examples: Mapped[List["DatasetExample"]] = relationship(back_populates="span")
@@ -10,7 +10,6 @@ from typing import (
10
10
  )
11
11
 
12
12
  from cachetools import LFUCache, TTLCache
13
- from openinference.semconv.trace import SpanAttributes
14
13
  from sqlalchemy import Select, func, select
15
14
  from sqlalchemy.sql.functions import coalesce
16
15
  from strawberry.dataloader import AbstractCache, DataLoader
@@ -107,8 +106,8 @@ def _get_stmt(
107
106
  *params: Param,
108
107
  ) -> Select[Any]:
109
108
  (start_time, end_time), filter_condition = segment
110
- prompt = func.sum(models.Span.attributes[_LLM_TOKEN_COUNT_PROMPT].as_float())
111
- completion = func.sum(models.Span.attributes[_LLM_TOKEN_COUNT_COMPLETION].as_float())
109
+ prompt = func.sum(models.Span.llm_token_count_prompt)
110
+ completion = func.sum(models.Span.llm_token_count_completion)
112
111
  total = coalesce(prompt, 0) + coalesce(completion, 0)
113
112
  pid = models.Trace.project_rowid
114
113
  stmt: Select[Any] = (
@@ -130,7 +129,3 @@ def _get_stmt(
130
129
  stmt = sf(stmt)
131
130
  stmt = stmt.where(pid.in_([rowid for rowid, _ in params]))
132
131
  return stmt
133
-
134
-
135
- _LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT.split(".")
136
- _LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION.split(".")
@@ -3,7 +3,6 @@ from enum import Enum, auto
3
3
  from typing import Any, Optional, Protocol
4
4
 
5
5
  import strawberry
6
- from openinference.semconv.trace import SpanAttributes
7
6
  from sqlalchemy import and_, desc, nulls_last
8
7
  from sqlalchemy.orm import InstrumentedAttribute
9
8
  from sqlalchemy.sql.expression import Select
@@ -16,10 +15,6 @@ from phoenix.server.api.types.pagination import CursorSortColumnDataType
16
15
  from phoenix.server.api.types.SortDir import SortDir
17
16
  from phoenix.trace.schemas import SpanID
18
17
 
19
- LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT.split(".")
20
- LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION.split(".")
21
- LLM_TOKEN_COUNT_TOTAL = SpanAttributes.LLM_TOKEN_COUNT_TOTAL.split(".")
22
-
23
18
 
24
19
  @strawberry.enum
25
20
  class SpanColumn(Enum):
@@ -47,11 +42,11 @@ class SpanColumn(Enum):
47
42
  elif self is SpanColumn.latencyMs:
48
43
  expr = models.Span.latency_ms
49
44
  elif self is SpanColumn.tokenCountTotal:
50
- expr = models.Span.attributes[LLM_TOKEN_COUNT_TOTAL].as_float()
45
+ expr = models.Span.llm_token_count_total
51
46
  elif self is SpanColumn.tokenCountPrompt:
52
- expr = models.Span.attributes[LLM_TOKEN_COUNT_PROMPT].as_float()
47
+ expr = models.Span.llm_token_count_prompt
53
48
  elif self is SpanColumn.tokenCountCompletion:
54
- expr = models.Span.attributes[LLM_TOKEN_COUNT_COMPLETION].as_float()
49
+ expr = models.Span.llm_token_count_completion
55
50
  elif self is SpanColumn.cumulativeTokenCountTotal:
56
51
  expr = (
57
52
  models.Span.cumulative_llm_token_count_prompt
@@ -40,9 +40,6 @@ EMBEDDING_EMBEDDINGS = SpanAttributes.EMBEDDING_EMBEDDINGS
40
40
  EMBEDDING_VECTOR = EmbeddingAttributes.EMBEDDING_VECTOR
41
41
  INPUT_MIME_TYPE = SpanAttributes.INPUT_MIME_TYPE
42
42
  INPUT_VALUE = SpanAttributes.INPUT_VALUE
43
- LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION
44
- LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT
45
- LLM_TOKEN_COUNT_TOTAL = SpanAttributes.LLM_TOKEN_COUNT_TOTAL
46
43
  LLM_PROMPT_TEMPLATE_VARIABLES = SpanAttributes.LLM_PROMPT_TEMPLATE_VARIABLES
47
44
  LLM_INPUT_MESSAGES = SpanAttributes.LLM_INPUT_MESSAGES
48
45
  LLM_OUTPUT_MESSAGES = SpanAttributes.LLM_OUTPUT_MESSAGES
@@ -308,18 +305,9 @@ def to_gql_span(span: models.Span) -> Span:
308
305
  attributes=json.dumps(_hide_embedding_vectors(span.attributes), cls=_JSONEncoder),
309
306
  metadata=_convert_metadata_to_string(get_attribute_value(span.attributes, METADATA)),
310
307
  num_documents=num_documents,
311
- token_count_total=cast(
312
- Optional[int],
313
- get_attribute_value(span.attributes, LLM_TOKEN_COUNT_TOTAL),
314
- ),
315
- token_count_prompt=cast(
316
- Optional[int],
317
- get_attribute_value(span.attributes, LLM_TOKEN_COUNT_PROMPT),
318
- ),
319
- token_count_completion=cast(
320
- Optional[int],
321
- get_attribute_value(span.attributes, LLM_TOKEN_COUNT_COMPLETION),
322
- ),
308
+ token_count_total=span.llm_token_count_total,
309
+ token_count_prompt=span.llm_token_count_prompt,
310
+ token_count_completion=span.llm_token_count_completion,
323
311
  cumulative_token_count_total=span.cumulative_llm_token_count_prompt
324
312
  + span.cumulative_llm_token_count_completion,
325
313
  cumulative_token_count_prompt=span.cumulative_llm_token_count_prompt,
@@ -1,22 +1,22 @@
1
1
  {
2
- "_components-CAummAJx.js": {
3
- "file": "assets/components-CAummAJx.js",
2
+ "_components-D2V-mOGq.js": {
3
+ "file": "assets/components-D2V-mOGq.js",
4
4
  "name": "components",
5
5
  "imports": [
6
6
  "_vendor-BMWfu6zp.js",
7
- "_vendor-arizeai-CkyzG9Wl.js",
8
- "_pages-BU__X1UX.js",
7
+ "_vendor-arizeai-Sj74jm5V.js",
8
+ "_pages-CChOjmat.js",
9
9
  "_vendor-three-DwGkEfCM.js",
10
10
  "_vendor-codemirror-DO3VqEcD.js"
11
11
  ]
12
12
  },
13
- "_pages-BU__X1UX.js": {
14
- "file": "assets/pages-BU__X1UX.js",
13
+ "_pages-CChOjmat.js": {
14
+ "file": "assets/pages-CChOjmat.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
17
  "_vendor-BMWfu6zp.js",
18
- "_components-CAummAJx.js",
19
- "_vendor-arizeai-CkyzG9Wl.js",
18
+ "_components-D2V-mOGq.js",
19
+ "_vendor-arizeai-Sj74jm5V.js",
20
20
  "_vendor-recharts-BGN0SxgJ.js",
21
21
  "_vendor-codemirror-DO3VqEcD.js"
22
22
  ]
@@ -35,8 +35,8 @@
35
35
  "assets/vendor-DxkFTwjz.css"
36
36
  ]
37
37
  },
38
- "_vendor-arizeai-CkyzG9Wl.js": {
39
- "file": "assets/vendor-arizeai-CkyzG9Wl.js",
38
+ "_vendor-arizeai-Sj74jm5V.js": {
39
+ "file": "assets/vendor-arizeai-Sj74jm5V.js",
40
40
  "name": "vendor-arizeai",
41
41
  "imports": [
42
42
  "_vendor-BMWfu6zp.js"
@@ -61,15 +61,15 @@
61
61
  "name": "vendor-three"
62
62
  },
63
63
  "index.tsx": {
64
- "file": "assets/index-Cg5hdf3g.js",
64
+ "file": "assets/index-B52Z3aZG.js",
65
65
  "name": "index",
66
66
  "src": "index.tsx",
67
67
  "isEntry": true,
68
68
  "imports": [
69
69
  "_vendor-BMWfu6zp.js",
70
- "_vendor-arizeai-CkyzG9Wl.js",
71
- "_components-CAummAJx.js",
72
- "_pages-BU__X1UX.js",
70
+ "_vendor-arizeai-Sj74jm5V.js",
71
+ "_components-D2V-mOGq.js",
72
+ "_pages-CChOjmat.js",
73
73
  "_vendor-three-DwGkEfCM.js",
74
74
  "_vendor-codemirror-DO3VqEcD.js",
75
75
  "_vendor-recharts-BGN0SxgJ.js"