vedana-backoffice 0.1.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.
- vedana_backoffice/Caddyfile +17 -0
- vedana_backoffice/__init__.py +0 -0
- vedana_backoffice/components/__init__.py +0 -0
- vedana_backoffice/components/etl_graph.py +132 -0
- vedana_backoffice/components/ui_chat.py +236 -0
- vedana_backoffice/graph/__init__.py +0 -0
- vedana_backoffice/graph/build.py +169 -0
- vedana_backoffice/pages/__init__.py +0 -0
- vedana_backoffice/pages/chat.py +204 -0
- vedana_backoffice/pages/etl.py +353 -0
- vedana_backoffice/pages/eval.py +1006 -0
- vedana_backoffice/pages/jims_thread_list_page.py +894 -0
- vedana_backoffice/pages/main_dashboard.py +483 -0
- vedana_backoffice/py.typed +0 -0
- vedana_backoffice/start_services.py +39 -0
- vedana_backoffice/state.py +0 -0
- vedana_backoffice/states/__init__.py +0 -0
- vedana_backoffice/states/chat.py +368 -0
- vedana_backoffice/states/common.py +66 -0
- vedana_backoffice/states/etl.py +1590 -0
- vedana_backoffice/states/eval.py +1940 -0
- vedana_backoffice/states/jims.py +508 -0
- vedana_backoffice/states/main_dashboard.py +757 -0
- vedana_backoffice/ui.py +115 -0
- vedana_backoffice/util.py +71 -0
- vedana_backoffice/vedana_backoffice.py +23 -0
- vedana_backoffice-0.1.0.dist-info/METADATA +10 -0
- vedana_backoffice-0.1.0.dist-info/RECORD +30 -0
- vedana_backoffice-0.1.0.dist-info/WHEEL +4 -0
- vedana_backoffice-0.1.0.dist-info/entry_points.txt +2 -0
vedana_backoffice/ui.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import reflex as rx
|
|
6
|
+
|
|
7
|
+
from vedana_backoffice.states.chat import ChatState
|
|
8
|
+
from vedana_backoffice.states.common import AppVersionState, TelegramBotState
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def telegram_link_box() -> rx.Component:
|
|
12
|
+
return rx.box(
|
|
13
|
+
rx.cond(
|
|
14
|
+
TelegramBotState.has_bot,
|
|
15
|
+
rx.link(
|
|
16
|
+
rx.hstack(
|
|
17
|
+
rx.text("Telegram", font_size="1.1em"),
|
|
18
|
+
# rx.icon("external-link", stroke_width=1, size=10),
|
|
19
|
+
# spacing="1",
|
|
20
|
+
),
|
|
21
|
+
href=TelegramBotState.bot_url,
|
|
22
|
+
is_external=True,
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
on_mount=TelegramBotState.load_bot_info,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def data_model_reload_btn() -> rx.Component:
|
|
30
|
+
return rx.button(
|
|
31
|
+
"Reload Data Model",
|
|
32
|
+
variant="soft",
|
|
33
|
+
color_scheme="blue",
|
|
34
|
+
on_click=ChatState.reload_data_model,
|
|
35
|
+
loading=ChatState.is_refreshing_dm,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def app_header() -> rx.Component:
|
|
40
|
+
return rx.box(
|
|
41
|
+
rx.hstack(
|
|
42
|
+
rx.hstack(
|
|
43
|
+
rx.link("Vedana Backoffice", href="/", font_weight="bold", font_size="1.25em"),
|
|
44
|
+
rx.markdown(AppVersionState.version), # type: ignore[operator]
|
|
45
|
+
align="center",
|
|
46
|
+
spacing="3",
|
|
47
|
+
),
|
|
48
|
+
rx.hstack(
|
|
49
|
+
data_model_reload_btn(),
|
|
50
|
+
rx.link("ETL", href="/etl", font_size="1.1em"),
|
|
51
|
+
rx.cond(
|
|
52
|
+
AppVersionState.eval_enabled,
|
|
53
|
+
rx.link("Eval", href="/eval", font_size="1.1em"),
|
|
54
|
+
rx.fragment(),
|
|
55
|
+
),
|
|
56
|
+
rx.link("Chat", href="/chat", font_size="1.1em"),
|
|
57
|
+
rx.link("JIMS", href="/jims", font_size="1.1em"),
|
|
58
|
+
telegram_link_box(),
|
|
59
|
+
rx.color_mode.button(), # type: ignore[attr-defined]
|
|
60
|
+
spacing="6",
|
|
61
|
+
align="center",
|
|
62
|
+
),
|
|
63
|
+
justify="between",
|
|
64
|
+
align="center",
|
|
65
|
+
width="100%",
|
|
66
|
+
),
|
|
67
|
+
width="100%",
|
|
68
|
+
padding="0.5em 1.25em",
|
|
69
|
+
border_bottom="1px solid #e5e7eb",
|
|
70
|
+
position="sticky",
|
|
71
|
+
top="0",
|
|
72
|
+
background_color=rx.color_mode_cond(light="white", dark="black"),
|
|
73
|
+
style={
|
|
74
|
+
"backdrop-filter": "blur(10px)", # enables non-transparent background
|
|
75
|
+
},
|
|
76
|
+
z_index="10",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def themed_data_table(
|
|
81
|
+
*,
|
|
82
|
+
data: rx.Var | list[Any],
|
|
83
|
+
columns: rx.Var | list[str],
|
|
84
|
+
width: str | rx.Var = "fit-content",
|
|
85
|
+
max_width: str | rx.Var = "100%",
|
|
86
|
+
**kwargs: Any,
|
|
87
|
+
) -> rx.Component:
|
|
88
|
+
"""Wrap rx.data_table with shared styling so it matches the app theme."""
|
|
89
|
+
|
|
90
|
+
default_kwargs = {"pagination": True, "search": True, "sort": True}
|
|
91
|
+
table_kwargs = {**default_kwargs, **kwargs}
|
|
92
|
+
|
|
93
|
+
table_style = table_kwargs.pop("style", {})
|
|
94
|
+
table_style = {"width": "fit-content", "minWidth": "100%", **table_style}
|
|
95
|
+
|
|
96
|
+
container_style: dict[str, Any] = {
|
|
97
|
+
"width": width,
|
|
98
|
+
"maxWidth": max_width,
|
|
99
|
+
"minWidth": "fit-content",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return rx.box(
|
|
103
|
+
rx.data_table(data=data, columns=columns, style=table_style, **table_kwargs),
|
|
104
|
+
class_name="datatable-surface",
|
|
105
|
+
style=container_style,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def breadcrumbs(items: list[tuple[str, str]]) -> rx.Component:
|
|
110
|
+
parts: list[rx.Component] = []
|
|
111
|
+
for idx, (label, href) in enumerate(items):
|
|
112
|
+
parts.append(rx.link(label, href=href))
|
|
113
|
+
if idx < len(items) - 1:
|
|
114
|
+
parts.append(rx.text("→", color="#9ca3af"))
|
|
115
|
+
return rx.hstack(*parts, spacing="2", padding_y="0.5em")
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import orjson as json
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def datetime_to_age(created_at: datetime, compact: bool = True) -> str:
|
|
8
|
+
"""Convert a datetime to a human-readable age string.
|
|
9
|
+
|
|
10
|
+
For datetimes less than 7 days old, returns relative time.
|
|
11
|
+
For older datetimes, returns absolute date format (e.g., "2025 Aug 24 14:30").
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
created_at: The datetime to convert
|
|
15
|
+
compact: If True, uses compact format (e.g., "2h", "3d14h").
|
|
16
|
+
If False, uses verbose format (e.g., "2 hours", "3 days 14 hours").
|
|
17
|
+
"""
|
|
18
|
+
now = datetime.now()
|
|
19
|
+
|
|
20
|
+
# created_at_dt = created_at
|
|
21
|
+
# if created_at_dt.tzinfo is None:
|
|
22
|
+
# created_at_dt = created_at_dt.replace(tzinfo=timezone.utc)
|
|
23
|
+
# time_diff = now - created_at_dt
|
|
24
|
+
|
|
25
|
+
time_diff = now - created_at
|
|
26
|
+
|
|
27
|
+
if time_diff.days < 7:
|
|
28
|
+
# Human readable relative time
|
|
29
|
+
if time_diff.days > 0:
|
|
30
|
+
hours = time_diff.seconds // 3600
|
|
31
|
+
if hours > 0:
|
|
32
|
+
if compact:
|
|
33
|
+
return f"{time_diff.days}d{hours}h"
|
|
34
|
+
else:
|
|
35
|
+
return f"{time_diff.days} days {hours} hours"
|
|
36
|
+
else:
|
|
37
|
+
if compact:
|
|
38
|
+
return f"{time_diff.days}d"
|
|
39
|
+
else:
|
|
40
|
+
return f"{time_diff.days} days"
|
|
41
|
+
elif time_diff.seconds >= 3600:
|
|
42
|
+
hours = time_diff.seconds // 3600
|
|
43
|
+
if compact:
|
|
44
|
+
return f"{hours}h"
|
|
45
|
+
else:
|
|
46
|
+
return f"{hours} hours"
|
|
47
|
+
elif time_diff.seconds >= 60:
|
|
48
|
+
minutes = time_diff.seconds // 60
|
|
49
|
+
if compact:
|
|
50
|
+
return f"{minutes}m"
|
|
51
|
+
else:
|
|
52
|
+
return f"{minutes} minutes"
|
|
53
|
+
else:
|
|
54
|
+
if compact:
|
|
55
|
+
return "1m"
|
|
56
|
+
else:
|
|
57
|
+
return "1 minute"
|
|
58
|
+
else:
|
|
59
|
+
# Absolute date format
|
|
60
|
+
return created_at.strftime("%Y %b %d %H:%M")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def safe_render_value(v: Any) -> str:
|
|
64
|
+
try:
|
|
65
|
+
s = json.dumps(v).decode() if isinstance(v, (dict, list)) else str(v)
|
|
66
|
+
except Exception:
|
|
67
|
+
try:
|
|
68
|
+
s = repr(v)
|
|
69
|
+
except Exception:
|
|
70
|
+
s = "<error rendering value>"
|
|
71
|
+
return s
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import reflex as rx
|
|
2
|
+
|
|
3
|
+
from vedana_backoffice.pages.chat import page as chat_page
|
|
4
|
+
from vedana_backoffice.pages.etl import page as etl_page
|
|
5
|
+
from vedana_backoffice.pages.jims_thread_list_page import jims_thread_list_page
|
|
6
|
+
from vedana_backoffice.pages.main_dashboard import page as main_dashboard_page
|
|
7
|
+
from vedana_backoffice.states.chat import ChatState
|
|
8
|
+
from vedana_backoffice.states.common import EVAL_ENABLED
|
|
9
|
+
from vedana_backoffice.states.etl import EtlState
|
|
10
|
+
from vedana_backoffice.states.main_dashboard import DashboardState
|
|
11
|
+
|
|
12
|
+
app = rx.App(stylesheets=["/styles.css"])
|
|
13
|
+
app.add_page(main_dashboard_page, route="/", title="Vedana Backoffice", on_load=DashboardState.load_dashboard)
|
|
14
|
+
app.add_page(etl_page, route="/etl", title="ETL", on_load=EtlState.load_pipeline_metadata)
|
|
15
|
+
app.add_page(chat_page, route="/chat", title="Chat", on_load=ChatState.reset_session)
|
|
16
|
+
app.add_page(jims_thread_list_page, route="/jims", title="JIMS")
|
|
17
|
+
|
|
18
|
+
# Eval page is only available when GRIST_TEST_SET_DOC_ID is configured
|
|
19
|
+
if EVAL_ENABLED:
|
|
20
|
+
from vedana_backoffice.pages.eval import page as eval_page
|
|
21
|
+
from vedana_backoffice.states.eval import EvalState
|
|
22
|
+
|
|
23
|
+
app.add_page(eval_page, route="/eval", title="Evaluation", on_load=EvalState.load_eval_data)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vedana-backoffice
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reflex-based admin/backoffice UI for Vedana
|
|
5
|
+
Author-email: Andrey Tatarinov <a@tatarinov.co>, Timur Sheydaev <tsheyd@epoch8.co>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: orjson>=3.11.3
|
|
8
|
+
Requires-Dist: reflex<0.9.0,>=0.8.25
|
|
9
|
+
Requires-Dist: vedana-core
|
|
10
|
+
Requires-Dist: vedana-etl
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
vedana_backoffice/Caddyfile,sha256=jZlD85_2e_eWggNFAtzkWN75uKuoHSbwWgeyWwibYhA,317
|
|
2
|
+
vedana_backoffice/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
vedana_backoffice/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
vedana_backoffice/start_services.py,sha256=94k2xrgIYXWhRfurR7XM-uMoXXL09RG0HFzyRRh9Nrs,974
|
|
5
|
+
vedana_backoffice/state.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
vedana_backoffice/ui.py,sha256=yKPAwUJZ_8cYzswqCV7nUxihvcrHw_DszeI1lbTaTbM,3618
|
|
7
|
+
vedana_backoffice/util.py,sha256=RVN9ezjNkSnrlT3pBIrSvQQLyf-TZ2WzP_AYxRGks_Q,2209
|
|
8
|
+
vedana_backoffice/vedana_backoffice.py,sha256=WlwF8hTBmkYOlgj9vE3k58MqDcj8FIbMNXZRsUXnjKg,1224
|
|
9
|
+
vedana_backoffice/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
vedana_backoffice/components/etl_graph.py,sha256=m3eFU8NO2z0GCMBnaj4I8a30AG7uerRq30sead8re9A,5256
|
|
11
|
+
vedana_backoffice/components/ui_chat.py,sha256=wpwSz-IP8uthKQEtrarZOZ4Pfg9sENboadic6bBlsj0,7746
|
|
12
|
+
vedana_backoffice/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
vedana_backoffice/graph/build.py,sha256=5BnL45bPGpNXLvsR6H05WFUCoqKMS0jj_MrcCZjX8N0,5706
|
|
14
|
+
vedana_backoffice/pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
vedana_backoffice/pages/chat.py,sha256=h0bjV_I8zi9ifX8jKPigBsHw-nnYMrhkoijN_VTSyXA,8891
|
|
16
|
+
vedana_backoffice/pages/etl.py,sha256=oms6KqEcgTujx8l8akgAbJIBAZik1brZ5YXF8IoBxWY,13967
|
|
17
|
+
vedana_backoffice/pages/eval.py,sha256=jEkiIs4xiyUx-k24qsIHLzxKGZHHDq5FjLmI9FpPo90,39307
|
|
18
|
+
vedana_backoffice/pages/jims_thread_list_page.py,sha256=ZogjE7XQW2Zuyzcy4mod8qQECvtPOSuJv5CkUsGVfeM,36561
|
|
19
|
+
vedana_backoffice/pages/main_dashboard.py,sha256=odi4V5Bf-qukPLN9f8whgaYWikL6D7v5kzqB4rm3T10,21350
|
|
20
|
+
vedana_backoffice/states/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
vedana_backoffice/states/chat.py,sha256=xZwnqhzVIhnfGRPAWPcgZg9_boM3Crvmu-gieh1QMfs,13980
|
|
22
|
+
vedana_backoffice/states/common.py,sha256=wI3ZCDeuV4_pkVQ-Q_Z0lgC8TQPs_eIT2nPqD-5rtjs,1977
|
|
23
|
+
vedana_backoffice/states/etl.py,sha256=1eLahWMm0Zd3oBDpOAJ_rfqHMPgRIJ_InwYzhm52hoo,68261
|
|
24
|
+
vedana_backoffice/states/eval.py,sha256=nDfDL5ifL_q2vWWBEPwtWomUG_dhJxOMlIWOndOtjxM,76897
|
|
25
|
+
vedana_backoffice/states/jims.py,sha256=gM0iZInD40-AebWY9gOzn5oJjAx7wyA_QliTluzMoZ8,19927
|
|
26
|
+
vedana_backoffice/states/main_dashboard.py,sha256=9AcWo2nl5L-hTymxr-Stbu2s5nTGl9CVxTA9mHu8Rt4,32388
|
|
27
|
+
vedana_backoffice-0.1.0.dist-info/METADATA,sha256=pVGuXZu-o9ly1pKf5iFjAllvTOPD3iMYXGpGNNmFfCI,341
|
|
28
|
+
vedana_backoffice-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
29
|
+
vedana_backoffice-0.1.0.dist-info/entry_points.txt,sha256=WyUmp5EmDq_dB9RjzEim4L-XmDhPDxYI7ptab4A1EJc,87
|
|
30
|
+
vedana_backoffice-0.1.0.dist-info/RECORD,,
|