trulens-dashboard 1.0.1__tar.gz

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.
Files changed (27) hide show
  1. trulens_dashboard-1.0.1/PKG-INFO +42 -0
  2. trulens_dashboard-1.0.1/README.md +9 -0
  3. trulens_dashboard-1.0.1/pyproject.toml +47 -0
  4. trulens_dashboard-1.0.1/trulens/dashboard/Leaderboard.py +312 -0
  5. trulens_dashboard-1.0.1/trulens/dashboard/__init__.py +13 -0
  6. trulens_dashboard-1.0.1/trulens/dashboard/appui.py +432 -0
  7. trulens_dashboard-1.0.1/trulens/dashboard/components/__init__.py +0 -0
  8. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/__init__.py +80 -0
  9. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/dist/assets/SourceCodePro-Bold-839f42df.ttf +0 -0
  10. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/dist/assets/SourceCodePro-Regular-6e5dd049.ttf +0 -0
  11. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/dist/assets/SourceCodePro-SemiBold-ccfba566.ttf +0 -0
  12. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/dist/assets/index-00b8e16d.css +1 -0
  13. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/dist/assets/index-923e1c7a.js +236 -0
  14. trulens_dashboard-1.0.1/trulens/dashboard/components/record_viewer/dist/index.html +14 -0
  15. trulens_dashboard-1.0.1/trulens/dashboard/display.py +76 -0
  16. trulens_dashboard-1.0.1/trulens/dashboard/notebook_utils.py +58 -0
  17. trulens_dashboard-1.0.1/trulens/dashboard/pages/Evaluations.py +599 -0
  18. trulens_dashboard-1.0.1/trulens/dashboard/pages/__init__.py +0 -0
  19. trulens_dashboard-1.0.1/trulens/dashboard/py.typed +0 -0
  20. trulens_dashboard-1.0.1/trulens/dashboard/run.py +306 -0
  21. trulens_dashboard-1.0.1/trulens/dashboard/streamlit.py +249 -0
  22. trulens_dashboard-1.0.1/trulens/dashboard/streamlit_utils.py +32 -0
  23. trulens_dashboard-1.0.1/trulens/dashboard/ux/__init__.py +0 -0
  24. trulens_dashboard-1.0.1/trulens/dashboard/ux/components.py +302 -0
  25. trulens_dashboard-1.0.1/trulens/dashboard/ux/page_config.py +72 -0
  26. trulens_dashboard-1.0.1/trulens/dashboard/ux/styles.py +150 -0
  27. trulens_dashboard-1.0.1/trulens/dashboard/ux/trulens_logo.svg +44 -0
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.1
2
+ Name: trulens-dashboard
3
+ Version: 1.0.1
4
+ Summary: Library to systematically track and evaluate LLM based applications.
5
+ Home-page: https://trulens.org/
6
+ License: MIT
7
+ Author: Snowflake Inc.
8
+ Author-email: ml-observability-wg-dl@snowflake.com
9
+ Requires-Python: >=3.9, !=2.7.*, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Dist: ipywidgets (>=8.1.3,<9.0.0)
19
+ Requires-Dist: jupyter (>=1,<2)
20
+ Requires-Dist: pandas (>=2.1,<3.0)
21
+ Requires-Dist: psutil (>=5.9,<6.0)
22
+ Requires-Dist: rich (>=13.6,<14.0)
23
+ Requires-Dist: streamlit (>=1.33,<2.0)
24
+ Requires-Dist: streamlit-aggrid (==0.3.4)
25
+ Requires-Dist: streamlit-extras (>=0.4,<0.5)
26
+ Requires-Dist: streamlit-pills (>=0.3,<0.4)
27
+ Requires-Dist: traitlets (>=5.0.5,<6.0.0)
28
+ Requires-Dist: trulens-core (>=1.0.0,<2.0.0)
29
+ Project-URL: Documentation, https://trulens.org/trulens/getting_started/
30
+ Project-URL: Repository, https://github.com/truera/trulens
31
+ Description-Content-Type: text/markdown
32
+
33
+ # trulens-dashboard
34
+
35
+ ## Install Node.js
36
+
37
+ See [the Node.js website](https://nodejs.org/en/download/package-manager) for details
38
+
39
+ ## Building
40
+
41
+ Build with `python -m build`
42
+
@@ -0,0 +1,9 @@
1
+ # trulens-dashboard
2
+
3
+ ## Install Node.js
4
+
5
+ See [the Node.js website](https://nodejs.org/en/download/package-manager) for details
6
+
7
+ ## Building
8
+
9
+ Build with `python -m build`
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ build-backend = "poetry.core.masonry.api"
3
+ requires = [
4
+ "poetry-core",
5
+ ]
6
+
7
+ [tool.poetry]
8
+ name = "trulens-dashboard"
9
+ version = "1.0.1"
10
+ description = "Library to systematically track and evaluate LLM based applications."
11
+ authors = [
12
+ "Snowflake Inc. <ml-observability-wg-dl@snowflake.com>",
13
+ ]
14
+ license = "MIT"
15
+ readme = "README.md"
16
+ packages = [
17
+ { include = "trulens" },
18
+ ]
19
+ homepage = "https://trulens.org/"
20
+ documentation = "https://trulens.org/trulens/getting_started/"
21
+ repository = "https://github.com/truera/trulens"
22
+ classifiers = [
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Operating System :: OS Independent",
28
+ "Development Status :: 3 - Alpha",
29
+ "License :: OSI Approved :: MIT License",
30
+ ]
31
+
32
+ [tool.poetry.dependencies]
33
+ python = "^3.9,!=3.9.7"
34
+ trulens-core = { version = "^1.0.0" }
35
+ ipywidgets = "^8.1.3"
36
+ jupyter = "^1"
37
+ pandas = "^2.1"
38
+ psutil = "^5.9"
39
+ rich = "^13.6"
40
+ streamlit = "^1.33"
41
+ streamlit-aggrid = "==0.3.4"
42
+ streamlit-extras = "^0.4"
43
+ streamlit-pills = "^0.3"
44
+ traitlets = "^5.0.5"
45
+
46
+ [tool.poetry.group.dev.dependencies]
47
+ trulens-core = { path = "../core" }
@@ -0,0 +1,312 @@
1
+ import asyncio
2
+ import json
3
+ import math
4
+
5
+ import streamlit as st
6
+ from streamlit_extras.switch_page_button import switch_page
7
+ from trulens.core import TruSession
8
+ from trulens.core.database.legacy.migration import MIGRATION_UNKNOWN_STR
9
+ from trulens.core.utils.text import format_quantity
10
+ from trulens.dashboard.streamlit_utils import init_from_args
11
+ from trulens.dashboard.ux import styles
12
+ from trulens.dashboard.ux.components import draw_metadata_and_tags
13
+ from trulens.dashboard.ux.page_config import set_page_config
14
+ from trulens.dashboard.ux.styles import CATEGORY
15
+
16
+ # https://github.com/jerryjliu/llama_index/issues/7244:
17
+ asyncio.set_event_loop(asyncio.new_event_loop())
18
+
19
+ if __name__ == "__main__":
20
+ # If not imported, gets args from command line and creates Tru singleton
21
+ init_from_args()
22
+
23
+
24
+ def leaderboard():
25
+ """Render the leaderboard page."""
26
+
27
+ set_page_config(page_title="Leaderboard")
28
+
29
+ session = TruSession() # get singleton whether this file was imported or executed from command line.
30
+
31
+ lms = session.connector.db
32
+
33
+ # Set the title
34
+ st.title("App Leaderboard")
35
+
36
+ def get_data():
37
+ return lms.get_records_and_feedback([])
38
+
39
+ def get_apps():
40
+ return list(lms.get_apps())
41
+
42
+ # sort apps by name
43
+ def sort_selected_apps(selected_apps, records):
44
+ """
45
+ Sorts the selected_apps list based on the concatenation of app_name and app_version.
46
+
47
+ Parameters:
48
+ selected_apps (list): List of app_ids to be sorted.
49
+ apps (DataFrame): DataFrame containing app_id and app_json columns.
50
+
51
+ Returns:
52
+ list: Sorted list of selected_apps.
53
+ """
54
+ # Create a mapping from app_id to (app_name, app_version)
55
+ app_info = (
56
+ records.set_index("app_id")["app_json"]
57
+ .apply(
58
+ lambda x: (
59
+ json.loads(x).get("app_name"),
60
+ json.loads(x).get("app_version"),
61
+ )
62
+ )
63
+ .to_dict()
64
+ )
65
+
66
+ # Sort selected_apps based on the concatenation of app_name and app_version
67
+ selected_apps = [
68
+ app_id for app_id in selected_apps if app_id in app_info
69
+ ]
70
+ sorted_apps = sorted(
71
+ selected_apps,
72
+ key=lambda app_id: f"{app_info[app_id][0]}{app_info[app_id][1]}",
73
+ )
74
+
75
+ return sorted_apps
76
+
77
+ records, feedback_col_names = get_data()
78
+ records = records.sort_values(by="app_id")
79
+
80
+ apps = get_apps()
81
+ app_names = sorted(list(set(app["app_name"] for app in apps)))
82
+
83
+ selected_app_names = st.multiselect("Filter apps:", app_names, app_names)
84
+ selected_apps = [
85
+ app["app_id"]
86
+ for app in apps
87
+ if any(name in app["app_name"] for name in selected_app_names)
88
+ ]
89
+ st.session_state.app = selected_apps
90
+
91
+ # Filter app versions to only include those from selected apps
92
+ filtered_apps = [app for app in apps if app["app_id"] in selected_apps]
93
+ app_name_versions = sorted(
94
+ list(
95
+ set(
96
+ f"{app['app_name']} - {app['app_version']}"
97
+ for app in filtered_apps
98
+ )
99
+ )
100
+ )
101
+
102
+ selected_app_name_versions = st.multiselect(
103
+ "Filter by app version:", app_name_versions, app_name_versions
104
+ )
105
+ selected_apps = [
106
+ app["app_id"]
107
+ for app in apps
108
+ if f"{app['app_name']} - {app['app_version']}"
109
+ in selected_app_name_versions
110
+ ]
111
+ st.session_state.app = selected_apps
112
+
113
+ with st.expander("Advanced Filters"):
114
+ # get tag options
115
+ tags = []
116
+ for i in range(len(apps)):
117
+ tags.append(apps[i]["tags"])
118
+ unique_tags = sorted(list(set(tags)))
119
+ # select tags
120
+ selected_tags = st.multiselect("Filter tags:", unique_tags, unique_tags)
121
+
122
+ # filter to apps with selected tags
123
+ tag_selected_apps = [
124
+ app["app_id"]
125
+ for app in apps
126
+ if any(tag in app["tags"] for tag in selected_tags)
127
+ ]
128
+ selected_apps = list(set(selected_apps) & set(tag_selected_apps))
129
+ st.session_state.app = selected_apps
130
+
131
+ # get metadata options
132
+ metadata_keys_unique = set()
133
+ for app in apps:
134
+ metadata_keys_unique.update(app["metadata"].keys())
135
+ metadata_keys_unique = list(metadata_keys_unique)
136
+ # select metadata
137
+ metadata_options = {}
138
+ for metadata_key in metadata_keys_unique:
139
+ unique_values = set()
140
+ for i in range(len(apps)):
141
+ try:
142
+ unique_values.add(apps[i]["metadata"][metadata_key])
143
+ except (KeyError, TypeError):
144
+ pass
145
+ metadata_options[metadata_key] = list(unique_values)
146
+
147
+ # select metadata
148
+ metadata_selections = metadata_options.copy()
149
+ for metadata_key in metadata_options.keys():
150
+ metadata_selections[metadata_key] = st.multiselect(
151
+ "Filter " + metadata_key + ":",
152
+ sorted(metadata_options[metadata_key]),
153
+ sorted(metadata_options[metadata_key]),
154
+ )
155
+
156
+ # filter to apps with selected metadata
157
+ metadata_selected_apps = [
158
+ app["app_id"]
159
+ for app in apps
160
+ if all(
161
+ metadata_key not in app["metadata"]
162
+ or app["metadata"][metadata_key]
163
+ in metadata_selections[metadata_key]
164
+ for metadata_key in metadata_selections.keys()
165
+ )
166
+ ]
167
+
168
+ selected_apps = list(set(selected_apps) & set(metadata_selected_apps))
169
+ st.session_state.app = selected_apps
170
+
171
+ feedback_defs = lms.get_feedback_defs()
172
+ feedback_directions = {
173
+ (
174
+ row.feedback_json.get("supplied_name", "")
175
+ or row.feedback_json["implementation"]["name"]
176
+ ): row.feedback_json.get("higher_is_better", True)
177
+ for _, row in feedback_defs.iterrows()
178
+ }
179
+
180
+ if records.empty:
181
+ st.write("No records yet...")
182
+ return
183
+
184
+ if records.empty:
185
+ st.write("No records yet...")
186
+
187
+ st.markdown("""---""")
188
+
189
+ selected_apps = sort_selected_apps(selected_apps, records)
190
+
191
+ for app in selected_apps:
192
+ app_df = records.loc[records.app_id == app]
193
+ if app_df.empty:
194
+ continue
195
+ app_str = app_df["app_json"].iloc[0]
196
+ app_json = json.loads(app_str)
197
+ app_name = app_json["app_name"]
198
+ app_version = app_json["app_version"]
199
+ app_name_version = f"{app_name} - {app_version}"
200
+ metadata = app_json.get("metadata")
201
+ tags = app_json.get("tags")
202
+ # st.text('Metadata' + str(metadata))
203
+ st.header(app_name_version, help=draw_metadata_and_tags(metadata, tags))
204
+ app_feedback_col_names = [
205
+ col_name
206
+ for col_name in feedback_col_names
207
+ if not app_df[col_name].isna().all()
208
+ ]
209
+ col1, col2, col3, col4, *feedback_cols, col99 = st.columns(
210
+ 5 + len(app_feedback_col_names)
211
+ )
212
+ latency_mean = (
213
+ app_df["latency"]
214
+ .apply(lambda td: td if td != MIGRATION_UNKNOWN_STR else None)
215
+ .mean()
216
+ )
217
+
218
+ col1.metric("Records", len(app_df))
219
+ col2.metric(
220
+ "Average Latency (Seconds)",
221
+ (
222
+ f"{format_quantity(round(latency_mean, 5), precision=2)}"
223
+ if not math.isnan(latency_mean)
224
+ else "nan"
225
+ ),
226
+ )
227
+
228
+ # Filter for Snowflake credits costs
229
+ filtered_snowflake_credits_app_df = app_df[
230
+ app_df["cost_currency"] == "Snowflake credits"
231
+ ]
232
+
233
+ # Calculate the total cost for Snowflake credits
234
+ if len(filtered_snowflake_credits_app_df) > 0:
235
+ total_cost_snowflake_credits = (
236
+ filtered_snowflake_credits_app_df["total_cost"].dropna().sum()
237
+ )
238
+ col3.metric(
239
+ "Total Cost (Snowflake credits)",
240
+ f"{format_quantity(round(total_cost_snowflake_credits, 8), precision=5)}",
241
+ )
242
+ else:
243
+ col3.empty()
244
+ # Filter for USD costs
245
+ filtered_usd_app_df = app_df[app_df["cost_currency"] == "USD"]
246
+
247
+ if len(filtered_usd_app_df) > 0:
248
+ # Calculate the total cost for USD
249
+ total_cost_usd = filtered_usd_app_df["total_cost"].dropna().sum()
250
+ col3.metric(
251
+ "Total Cost (USD)",
252
+ f"${format_quantity(round(total_cost_usd, 5), precision=2)}",
253
+ )
254
+ else:
255
+ col3.empty()
256
+
257
+ col4.metric(
258
+ "Total Tokens",
259
+ format_quantity(
260
+ sum(
261
+ tokens
262
+ for tokens in app_df.total_tokens
263
+ if tokens is not None
264
+ ),
265
+ precision=2,
266
+ ),
267
+ )
268
+
269
+ for i, col_name in enumerate(app_feedback_col_names):
270
+ mean = app_df[col_name].mean()
271
+
272
+ st.write(
273
+ styles.stmetricdelta_hidearrow,
274
+ unsafe_allow_html=True,
275
+ )
276
+
277
+ higher_is_better = feedback_directions.get(col_name, True)
278
+
279
+ if "distance" in col_name:
280
+ feedback_cols[i].metric(
281
+ label=col_name,
282
+ value=f"{round(mean, 2)}",
283
+ delta_color="normal",
284
+ )
285
+ else:
286
+ cat = CATEGORY.of_score(mean, higher_is_better=higher_is_better)
287
+ feedback_cols[i].metric(
288
+ label=col_name,
289
+ value=f"{round(mean, 2)}",
290
+ delta=f"{cat.icon} {cat.adjective}",
291
+ delta_color=(
292
+ "normal"
293
+ if cat.compare(
294
+ mean, CATEGORY.PASS[cat.direction].threshold
295
+ )
296
+ else "inverse"
297
+ ),
298
+ )
299
+
300
+ with col99:
301
+ if st.button("Select App", key=f"app-selector-{app}"):
302
+ st.session_state.app = app
303
+ switch_page("Evaluations")
304
+
305
+ # with st.expander("Model metadata"):
306
+ # st.markdown(draw_metadata(metadata))
307
+
308
+ st.markdown("""---""")
309
+
310
+
311
+ if __name__ == "__main__":
312
+ leaderboard()
@@ -0,0 +1,13 @@
1
+ from importlib.metadata import version
2
+
3
+ from trulens.core.utils.imports import safe_importlib_package_name
4
+ from trulens.dashboard.run import run_dashboard
5
+ from trulens.dashboard.run import stop_dashboard
6
+
7
+ __version__ = version(safe_importlib_package_name(__package__ or __name__))
8
+
9
+
10
+ __all__ = [
11
+ "run_dashboard",
12
+ "stop_dashboard",
13
+ ]