vedana-backoffice 0.1.0__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.
- vedana_backoffice-0.1.0/.gitignore +6 -0
- vedana_backoffice-0.1.0/CHANGELOG.md +31 -0
- vedana_backoffice-0.1.0/PKG-INFO +10 -0
- vedana_backoffice-0.1.0/README.md +0 -0
- vedana_backoffice-0.1.0/assets/styles.css +120 -0
- vedana_backoffice-0.1.0/pyproject.toml +61 -0
- vedana_backoffice-0.1.0/rxconfig.py +9 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/Caddyfile +17 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/__init__.py +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/components/__init__.py +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/components/etl_graph.py +132 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/components/ui_chat.py +236 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/graph/__init__.py +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/graph/build.py +169 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/pages/__init__.py +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/pages/chat.py +204 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/pages/etl.py +353 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/pages/eval.py +1006 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/pages/jims_thread_list_page.py +894 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/pages/main_dashboard.py +483 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/py.typed +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/start_services.py +39 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/state.py +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/__init__.py +0 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/chat.py +368 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/common.py +66 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/etl.py +1590 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/eval.py +1940 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/jims.py +508 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/states/main_dashboard.py +757 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/ui.py +115 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/util.py +71 -0
- vedana_backoffice-0.1.0/src/vedana_backoffice/vedana_backoffice.py +23 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 0.4.0
|
|
2
|
+
|
|
3
|
+
* Splitting ETL pipelines into tabs by new pipeline label
|
|
4
|
+
* Improve data views: server-side pagination, custom styling
|
|
5
|
+
* Add stats to ETL step / data table cards - last run time, row changes, row counts
|
|
6
|
+
* Improve ETL Graph rendering logic
|
|
7
|
+
* Refactor backoffice state.py - split into smaller states per-page
|
|
8
|
+
* Improve pipeline details view in assistant messages
|
|
9
|
+
* Add pipeline logs to assistant message details on Chat page
|
|
10
|
+
* New pipeline tests/evaluation page: view golden dataset, run tests, compare results
|
|
11
|
+
* add LLM model selection to chat page - integration via openrouter
|
|
12
|
+
|
|
13
|
+
# 0.3.0 - 2025.11.20
|
|
14
|
+
|
|
15
|
+
* ETL - added monitoring dashboard on the main page
|
|
16
|
+
* General - UI improvements: layouts, tags filtering in JIMS thread viewer
|
|
17
|
+
* Chat - added total chat cost counter
|
|
18
|
+
* ETL - added row count and last run changes in DataTable view
|
|
19
|
+
|
|
20
|
+
* Add link to Telegram bot if `TELEGRAM_BOT_TOKEN` env is provided
|
|
21
|
+
|
|
22
|
+
# 0.2.0 - 2025.10.29
|
|
23
|
+
|
|
24
|
+
* JIMS thread viewer - add feedback processing flow, UI updates
|
|
25
|
+
|
|
26
|
+
# 0.1.0
|
|
27
|
+
|
|
28
|
+
Initial commit:
|
|
29
|
+
* ETL page - view and select steps / data tables and run Datapipe pipelines
|
|
30
|
+
* Chat page - basic chat UI
|
|
31
|
+
* JIMS thread viewer - view previous conversations, add feedback
|
|
@@ -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
|
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
.datatable-surface {
|
|
2
|
+
/* Width is controlled by inline styles or parent container */
|
|
3
|
+
display: block;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.datatable-surface .gridjs-container {
|
|
7
|
+
width: fit-content;
|
|
8
|
+
min-width: 100%;
|
|
9
|
+
background-color: var(--gray-2);
|
|
10
|
+
border: 1px solid var(--gray-5);
|
|
11
|
+
border-radius: 12px;
|
|
12
|
+
padding: 0.75rem;
|
|
13
|
+
color: var(--gray-12);
|
|
14
|
+
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.datatable-surface .gridjs-wrapper {
|
|
18
|
+
border-radius: 8px;
|
|
19
|
+
overflow: visible;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.datatable-surface .gridjs-table {
|
|
23
|
+
width: 100%;
|
|
24
|
+
border-collapse: collapse;
|
|
25
|
+
background-color: transparent;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.datatable-surface .gridjs-th,
|
|
29
|
+
.datatable-surface .gridjs-td {
|
|
30
|
+
border-color: var(--gray-5);
|
|
31
|
+
color: var(--gray-12);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.datatable-surface .gridjs-th {
|
|
35
|
+
background-color: var(--gray-3);
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.datatable-surface .gridjs-tr:nth-child(odd) .gridjs-td {
|
|
40
|
+
background-color: var(--gray-1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.datatable-surface .gridjs-tr:nth-child(even) .gridjs-td {
|
|
44
|
+
background-color: var(--gray-2);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.datatable-surface .gridjs-tr:hover .gridjs-td {
|
|
48
|
+
background-color: var(--accent-3);
|
|
49
|
+
transition: background-color 0.2s ease-in-out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.datatable-surface .gridjs-search-input,
|
|
53
|
+
.datatable-surface .gridjs-pagination button {
|
|
54
|
+
background-color: var(--gray-1);
|
|
55
|
+
color: var(--gray-12);
|
|
56
|
+
border: 1px solid var(--gray-5);
|
|
57
|
+
border-radius: 6px;
|
|
58
|
+
padding: 0.35rem 0.65rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.datatable-surface .gridjs-pagination .gridjs-currentPage {
|
|
62
|
+
background-color: var(--accent-4);
|
|
63
|
+
color: var(--accent-12);
|
|
64
|
+
border-color: transparent;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.datatable-surface .gridjs-footer {
|
|
68
|
+
background-color: var(--gray-2);
|
|
69
|
+
border-top: 1px solid var(--gray-5);
|
|
70
|
+
padding: 0.5rem 0.75rem;
|
|
71
|
+
color: var(--gray-12);
|
|
72
|
+
border-radius: 0 0 8px 8px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.datatable-surface .gridjs-footer .gridjs-pages {
|
|
76
|
+
gap: 0.25rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.datatable-surface .gridjs-footer .gridjs-pagination button {
|
|
80
|
+
background-color: var(--gray-1);
|
|
81
|
+
border-color: var(--gray-5);
|
|
82
|
+
color: var(--gray-12);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.datatable-surface .gridjs-footer .gridjs-pagination button:disabled {
|
|
86
|
+
background-color: var(--gray-4);
|
|
87
|
+
color: var(--gray-9);
|
|
88
|
+
border-color: var(--gray-6);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.datatable-surface .gridjs-footer .gridjs-pagination .gridjs-currentPage {
|
|
92
|
+
background-color: var(--accent-4);
|
|
93
|
+
color: var(--accent-12);
|
|
94
|
+
border-color: transparent;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.datatable-surface .gridjs-th:hover {
|
|
98
|
+
background-color: var(--gray-4);
|
|
99
|
+
color: var(--gray-12);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.datatable-surface .gridjs-pagination button:disabled {
|
|
103
|
+
opacity: 0.6;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.datatable-surface .gridjs-search-input::placeholder {
|
|
107
|
+
color: var(--gray-10);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.datatable-surface .gridjs-pagination .gridjs-pages button.gridjs-spread {
|
|
111
|
+
background-color: var(--gray-1) !important;
|
|
112
|
+
color: var(--gray-12) !important;
|
|
113
|
+
border-color: var(--gray-5) !important;
|
|
114
|
+
cursor: default;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.datatable-surface .gridjs-pagination .gridjs-pages button.gridjs-spread:hover {
|
|
118
|
+
background-color: var(--gray-3) !important;
|
|
119
|
+
border-color: var(--gray-6) !important;
|
|
120
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "vedana-backoffice"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Reflex-based admin/backoffice UI for Vedana"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Andrey Tatarinov", email = "a@tatarinov.co" },
|
|
9
|
+
{ name = "Timur Sheydaev", email = "tsheyd@epoch8.co" },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
dependencies = [
|
|
13
|
+
"reflex>=0.8.25,<0.9.0",
|
|
14
|
+
"orjson>=3.11.3",
|
|
15
|
+
"vedana-core",
|
|
16
|
+
"vedana-etl",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
vedana-backoffice-with-caddy = "vedana_backoffice.start_services:main"
|
|
21
|
+
|
|
22
|
+
[tool.uv.build.wheel]
|
|
23
|
+
packages = ["vedana_backoffice"]
|
|
24
|
+
|
|
25
|
+
include = [
|
|
26
|
+
{ path = "vedana_backoffice/Caddyfile" }
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling", "uv-dynamic-versioning"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.version]
|
|
34
|
+
source = "uv-dynamic-versioning"
|
|
35
|
+
|
|
36
|
+
[tool.uv-dynamic-versioning]
|
|
37
|
+
enable = true
|
|
38
|
+
vcs = "git"
|
|
39
|
+
pattern = "default-unprefixed"
|
|
40
|
+
pattern-prefix = "vedana-"
|
|
41
|
+
|
|
42
|
+
[tool.mypy]
|
|
43
|
+
plugins = ['pydantic.mypy']
|
|
44
|
+
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"ruff>=0.14.10",
|
|
48
|
+
"mypy>=1.19.0",
|
|
49
|
+
"pytest>=8.4.0",
|
|
50
|
+
"pandas-stubs==2.3.2.250926",
|
|
51
|
+
"types-requests>=2.32.4.20250913",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
line-length = 120
|
|
56
|
+
|
|
57
|
+
[tool.uv-workspace-codegen]
|
|
58
|
+
generate = true
|
|
59
|
+
template_type = ["lib", "publish"]
|
|
60
|
+
generate_standard_pytest_step = false
|
|
61
|
+
typechecker = "mypy"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
:9000
|
|
2
|
+
|
|
3
|
+
encode gzip
|
|
4
|
+
|
|
5
|
+
@backend_routes path /_event/* /ping /_upload /_upload/*
|
|
6
|
+
handle @backend_routes {
|
|
7
|
+
reverse_proxy localhost:8000 {
|
|
8
|
+
header_up Upgrade {http.request.header.upgrade}
|
|
9
|
+
header_up Connection {http.request.header.connection}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
root * /srv
|
|
14
|
+
route {
|
|
15
|
+
try_files {path} {path}/ /404.html
|
|
16
|
+
file_server
|
|
17
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import reflex as rx
|
|
2
|
+
|
|
3
|
+
from vedana_backoffice.states.etl import EtlState
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _node_card(node: dict) -> rx.Component:
|
|
7
|
+
is_table = node.get("node_type") == "table" # step for transform, table for table
|
|
8
|
+
return rx.card(
|
|
9
|
+
rx.vstack(
|
|
10
|
+
rx.hstack(
|
|
11
|
+
rx.hstack(
|
|
12
|
+
rx.cond(
|
|
13
|
+
is_table,
|
|
14
|
+
rx.box(),
|
|
15
|
+
rx.badge(node.get("step_type", ""), color_scheme="indigo", variant="soft"),
|
|
16
|
+
),
|
|
17
|
+
spacing="2",
|
|
18
|
+
),
|
|
19
|
+
rx.text(node.get("name", ""), weight="medium"),
|
|
20
|
+
justify="between",
|
|
21
|
+
width="100%",
|
|
22
|
+
),
|
|
23
|
+
rx.text(node.get("labels_str", ""), size="1", color="gray"),
|
|
24
|
+
rx.cond(
|
|
25
|
+
is_table,
|
|
26
|
+
rx.hstack(
|
|
27
|
+
rx.tooltip(rx.text(node.get("last_run", "—"), size="1", color="gray"), content="last update time"),
|
|
28
|
+
rx.hstack(
|
|
29
|
+
rx.tooltip(
|
|
30
|
+
rx.text(node.get("row_count", "—"), size="1", color="gray", weight="bold"),
|
|
31
|
+
content="rows total",
|
|
32
|
+
),
|
|
33
|
+
rx.tooltip(
|
|
34
|
+
rx.text(node.get("last_add", "—"), size="1", color="green"), content="added during last run"
|
|
35
|
+
),
|
|
36
|
+
rx.text("/", size="1", color="gray"),
|
|
37
|
+
rx.tooltip(
|
|
38
|
+
rx.text(node.get("last_upd", "—"), size="1", color="gray"),
|
|
39
|
+
content="updated during last run",
|
|
40
|
+
),
|
|
41
|
+
rx.text("/", size="1", color="gray"),
|
|
42
|
+
rx.tooltip(
|
|
43
|
+
rx.text(node.get("last_rm", "—"), size="1", color="red"), content="deleted during last run"
|
|
44
|
+
),
|
|
45
|
+
spacing="1",
|
|
46
|
+
),
|
|
47
|
+
width="100%",
|
|
48
|
+
justify="between",
|
|
49
|
+
),
|
|
50
|
+
# Step view: show last run time and rows processed
|
|
51
|
+
rx.cond(
|
|
52
|
+
node.get("step_type") != "BatchGenerate", # BatchGenerate has no meta table
|
|
53
|
+
rx.hstack(
|
|
54
|
+
rx.tooltip(
|
|
55
|
+
rx.text(node.get("last_run", "—"), size="1", color="gray"),
|
|
56
|
+
content="last run time (that produced changes)",
|
|
57
|
+
),
|
|
58
|
+
rx.hstack(
|
|
59
|
+
rx.tooltip(
|
|
60
|
+
rx.text(node.get("rows_processed", 0), size="1", color="gray"),
|
|
61
|
+
content="rows processed in last run",
|
|
62
|
+
),
|
|
63
|
+
rx.text("/", size="1", color="gray"),
|
|
64
|
+
rx.tooltip(
|
|
65
|
+
rx.text(node.get("total_success", 0), size="1", color="gray", weight="bold"),
|
|
66
|
+
content="rows processed total",
|
|
67
|
+
),
|
|
68
|
+
rx.cond(
|
|
69
|
+
node.get("has_total_failed", False),
|
|
70
|
+
rx.tooltip(
|
|
71
|
+
rx.text(node.get("total_failed_str", ""), size="1", color="red"),
|
|
72
|
+
content="total failed rows (all time)",
|
|
73
|
+
),
|
|
74
|
+
rx.box(),
|
|
75
|
+
),
|
|
76
|
+
spacing="1",
|
|
77
|
+
),
|
|
78
|
+
width="100%",
|
|
79
|
+
justify="between",
|
|
80
|
+
),
|
|
81
|
+
rx.box(),
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
spacing="2",
|
|
85
|
+
width="100%",
|
|
86
|
+
),
|
|
87
|
+
padding="0.75em",
|
|
88
|
+
style={
|
|
89
|
+
"position": "absolute",
|
|
90
|
+
"left": node.get("left", "0px"),
|
|
91
|
+
"top": node.get("top", "0px"),
|
|
92
|
+
"width": node.get("width", "420px"),
|
|
93
|
+
"height": "auto",
|
|
94
|
+
"border": node.get("border_css", "1px solid #e5e7eb"),
|
|
95
|
+
"overflow": "visible",
|
|
96
|
+
"boxSizing": "border-box",
|
|
97
|
+
},
|
|
98
|
+
variant="surface",
|
|
99
|
+
on_click=rx.cond(
|
|
100
|
+
is_table,
|
|
101
|
+
# no direct return values here and that's ok, handled in state
|
|
102
|
+
EtlState.preview_table(table_name=node.get("name", "")), # type: ignore
|
|
103
|
+
EtlState.toggle_node_selection(index=node.get("index_value")), # type: ignore
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def etl_graph() -> rx.Component:
|
|
109
|
+
svg = rx.box(
|
|
110
|
+
rx.html(EtlState.graph_svg),
|
|
111
|
+
style={
|
|
112
|
+
"position": "absolute",
|
|
113
|
+
"left": 0,
|
|
114
|
+
"top": 0,
|
|
115
|
+
"pointerEvents": "none",
|
|
116
|
+
"width": "100%",
|
|
117
|
+
"height": "100%",
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
nodes_layer = rx.box(
|
|
122
|
+
rx.foreach(EtlState.graph_nodes, _node_card),
|
|
123
|
+
style={
|
|
124
|
+
"position": "absolute",
|
|
125
|
+
"left": 0,
|
|
126
|
+
"top": 0,
|
|
127
|
+
"width": "100%",
|
|
128
|
+
"height": "100%",
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return rx.box(svg, nodes_layer, style={"position": "relative", "width": "100%", "height": "100%"})
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import reflex as rx
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def render_message_bubble(
|
|
5
|
+
msg: dict,
|
|
6
|
+
on_toggle_details,
|
|
7
|
+
extras: rx.Component | None = None,
|
|
8
|
+
corner_tags_component: rx.Component | None = None,
|
|
9
|
+
) -> rx.Component: # type: ignore[valid-type]
|
|
10
|
+
"""Render a chat-style message bubble.
|
|
11
|
+
|
|
12
|
+
Expects msg dict with keys:
|
|
13
|
+
- content, is_assistant (bool-like), created_at_fmt or created_at
|
|
14
|
+
- has_tech, has_logs, show_details
|
|
15
|
+
- has_models, has_vts, has_cypher, models_str, vts_str, cypher_str (optional)
|
|
16
|
+
- logs_str (optional)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
tech_block = rx.cond(
|
|
20
|
+
msg.get("has_tech"),
|
|
21
|
+
rx.card(
|
|
22
|
+
rx.vstack(
|
|
23
|
+
rx.cond(
|
|
24
|
+
msg.get("has_models"),
|
|
25
|
+
rx.vstack(
|
|
26
|
+
rx.text("Models", weight="medium"),
|
|
27
|
+
rx.code_block(
|
|
28
|
+
msg.get("models_str", ""),
|
|
29
|
+
font_size="11px",
|
|
30
|
+
language="json",
|
|
31
|
+
# (bug in reflex?) code_block does not pass some custom styling (wordBreak, whiteSpace)
|
|
32
|
+
# https://github.com/reflex-dev/reflex/issues/6051
|
|
33
|
+
code_tag_props={"style": {"whiteSpace": "pre-wrap"}},
|
|
34
|
+
style={
|
|
35
|
+
"display": "block",
|
|
36
|
+
"maxWidth": "100%",
|
|
37
|
+
"boxSizing": "border-box",
|
|
38
|
+
},
|
|
39
|
+
),
|
|
40
|
+
spacing="1",
|
|
41
|
+
width="100%",
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
rx.cond(
|
|
45
|
+
msg.get("has_vts"),
|
|
46
|
+
rx.vstack(
|
|
47
|
+
rx.text("VTS Queries", weight="medium"),
|
|
48
|
+
rx.code_block(
|
|
49
|
+
msg.get("vts_str", ""),
|
|
50
|
+
font_size="11px",
|
|
51
|
+
language="python",
|
|
52
|
+
code_tag_props={"style": {"whiteSpace": "pre-wrap"}},
|
|
53
|
+
style={
|
|
54
|
+
"display": "block",
|
|
55
|
+
"maxWidth": "100%",
|
|
56
|
+
"boxSizing": "border-box",
|
|
57
|
+
},
|
|
58
|
+
),
|
|
59
|
+
spacing="1",
|
|
60
|
+
width="100%",
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
rx.cond(
|
|
64
|
+
msg.get("has_cypher"),
|
|
65
|
+
rx.vstack(
|
|
66
|
+
rx.text("Cypher Queries", weight="medium"),
|
|
67
|
+
rx.code_block(
|
|
68
|
+
msg.get("cypher_str", ""),
|
|
69
|
+
font_size="11px",
|
|
70
|
+
language="cypher",
|
|
71
|
+
code_tag_props={"style": {"whiteSpace": "pre-wrap"}},
|
|
72
|
+
style={
|
|
73
|
+
"display": "block",
|
|
74
|
+
"maxWidth": "100%",
|
|
75
|
+
"boxSizing": "border-box",
|
|
76
|
+
},
|
|
77
|
+
),
|
|
78
|
+
spacing="1",
|
|
79
|
+
width="100%",
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
spacing="2",
|
|
83
|
+
width="100%",
|
|
84
|
+
),
|
|
85
|
+
padding="0.75em",
|
|
86
|
+
variant="surface",
|
|
87
|
+
),
|
|
88
|
+
rx.box(),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
generic_details_block = rx.cond(
|
|
92
|
+
msg.get("generic_meta"),
|
|
93
|
+
rx.code_block(
|
|
94
|
+
msg.get("event_data_str", ""),
|
|
95
|
+
font_size="11px",
|
|
96
|
+
language="json",
|
|
97
|
+
code_tag_props={"style": {"whiteSpace": "pre-wrap"}},
|
|
98
|
+
style={
|
|
99
|
+
"display": "block",
|
|
100
|
+
"maxWidth": "100%",
|
|
101
|
+
"boxSizing": "border-box",
|
|
102
|
+
},
|
|
103
|
+
),
|
|
104
|
+
rx.box(),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
logs_block = rx.cond(
|
|
108
|
+
msg.get("has_logs"),
|
|
109
|
+
rx.card(
|
|
110
|
+
rx.vstack(
|
|
111
|
+
rx.text("Logs", weight="medium"),
|
|
112
|
+
rx.scroll_area(
|
|
113
|
+
rx.code_block(
|
|
114
|
+
msg.get("logs_str", ""),
|
|
115
|
+
font_size="11px",
|
|
116
|
+
language="log",
|
|
117
|
+
wrap_long_lines=True,
|
|
118
|
+
style={
|
|
119
|
+
"display": "block",
|
|
120
|
+
"maxWidth": "100%",
|
|
121
|
+
"boxSizing": "border-box",
|
|
122
|
+
},
|
|
123
|
+
code_tag_props={"style": {"whiteSpace": "pre-wrap"}}, # styling workaround
|
|
124
|
+
),
|
|
125
|
+
type="always",
|
|
126
|
+
scrollbars="vertical",
|
|
127
|
+
style={
|
|
128
|
+
"maxHeight": "25vh",
|
|
129
|
+
"width": "100%",
|
|
130
|
+
},
|
|
131
|
+
),
|
|
132
|
+
spacing="1",
|
|
133
|
+
width="100%",
|
|
134
|
+
),
|
|
135
|
+
padding="0.75em",
|
|
136
|
+
width="100%",
|
|
137
|
+
variant="surface",
|
|
138
|
+
),
|
|
139
|
+
rx.box(),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
details_block = rx.vstack(
|
|
143
|
+
tech_block,
|
|
144
|
+
generic_details_block,
|
|
145
|
+
logs_block,
|
|
146
|
+
spacing="2",
|
|
147
|
+
width="100%",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Tag badges for feedback
|
|
151
|
+
tags_box = corner_tags_component or rx.box()
|
|
152
|
+
|
|
153
|
+
header_left = rx.hstack(
|
|
154
|
+
# event type label at the very left
|
|
155
|
+
rx.cond(
|
|
156
|
+
msg.get("tag_label", "") != "",
|
|
157
|
+
rx.badge(msg.get("tag_label", ""), variant="soft", size="1", color_scheme="gray"),
|
|
158
|
+
rx.box(),
|
|
159
|
+
),
|
|
160
|
+
rx.cond(
|
|
161
|
+
msg.get("has_tech") | msg.get("has_logs") | msg.get("generic_meta"), # type: ignore[operator]
|
|
162
|
+
rx.button(
|
|
163
|
+
"Details",
|
|
164
|
+
variant="ghost",
|
|
165
|
+
color_scheme="gray",
|
|
166
|
+
size="1",
|
|
167
|
+
on_click=on_toggle_details, # type: ignore[arg-type]
|
|
168
|
+
),
|
|
169
|
+
),
|
|
170
|
+
rx.text(msg.get("created_at", ""), size="1", color="gray"),
|
|
171
|
+
spacing="2",
|
|
172
|
+
align="center",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
body = rx.vstack(
|
|
176
|
+
rx.hstack(header_left, tags_box, justify="between", width="100%", align="center"), # header
|
|
177
|
+
rx.text(
|
|
178
|
+
msg.get("content", ""),
|
|
179
|
+
style={
|
|
180
|
+
"whiteSpace": "pre-wrap",
|
|
181
|
+
"wordBreak": "break-word",
|
|
182
|
+
},
|
|
183
|
+
),
|
|
184
|
+
rx.cond(msg.get("show_details"), details_block),
|
|
185
|
+
rx.cond(extras is not None, extras or rx.box()),
|
|
186
|
+
spacing="2",
|
|
187
|
+
width="100%",
|
|
188
|
+
style={
|
|
189
|
+
"maxWidth": "100%",
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
assistant_bubble = rx.card(
|
|
194
|
+
body,
|
|
195
|
+
padding="0.75em",
|
|
196
|
+
style={
|
|
197
|
+
"maxWidth": "70%", # 35 vw is 70% of 50% parent card width in vw terms
|
|
198
|
+
"backgroundColor": "#11182714",
|
|
199
|
+
"border": "1px solid #e5e7eb",
|
|
200
|
+
"borderRadius": "12px",
|
|
201
|
+
"wordBreak": "break-word",
|
|
202
|
+
"overflowX": "hidden",
|
|
203
|
+
},
|
|
204
|
+
)
|
|
205
|
+
user_bubble = rx.card(
|
|
206
|
+
body,
|
|
207
|
+
padding="0.75em",
|
|
208
|
+
style={
|
|
209
|
+
"maxWidth": "70%",
|
|
210
|
+
"backgroundColor": "#3b82f614",
|
|
211
|
+
"border": "1px solid #e5e7eb",
|
|
212
|
+
"borderRadius": "12px",
|
|
213
|
+
"wordBreak": "break-word",
|
|
214
|
+
"overflowX": "hidden",
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
return rx.cond(
|
|
219
|
+
msg.get("is_assistant"),
|
|
220
|
+
rx.hstack(
|
|
221
|
+
rx.avatar(fallback="A", size="2", radius="full"),
|
|
222
|
+
assistant_bubble,
|
|
223
|
+
spacing="2",
|
|
224
|
+
width="100%",
|
|
225
|
+
justify="start",
|
|
226
|
+
align="start",
|
|
227
|
+
),
|
|
228
|
+
rx.hstack(
|
|
229
|
+
user_bubble,
|
|
230
|
+
rx.avatar(fallback="U", size="2", radius="full"),
|
|
231
|
+
spacing="2",
|
|
232
|
+
width="100%",
|
|
233
|
+
justify="end",
|
|
234
|
+
align="start",
|
|
235
|
+
),
|
|
236
|
+
)
|
|
File without changes
|