jvcli 2.0.1__py3-none-any.whl → 2.0.3__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.
- jvcli/__init__.py +1 -1
- jvcli/cli.py +2 -0
- jvcli/client/__init__.py +1 -0
- jvcli/client/app.py +160 -0
- jvcli/client/lib/__init__.py +1 -0
- jvcli/client/lib/page.py +68 -0
- jvcli/client/lib/utils.py +334 -0
- jvcli/client/lib/widgets.py +252 -0
- jvcli/client/pages/__init__.py +1 -0
- jvcli/client/pages/analytics_page.py +178 -0
- jvcli/client/pages/chat_page.py +143 -0
- jvcli/client/pages/dashboard_page.py +119 -0
- jvcli/client/pages/graph_page.py +20 -0
- jvcli/commands/client.py +50 -0
- jvcli/commands/create.py +5 -5
- jvcli/commands/publish.py +12 -1
- jvcli/commands/studio.py +1 -1
- {jvcli-2.0.1.dist-info → jvcli-2.0.3.dist-info}/METADATA +5 -1
- jvcli-2.0.3.dist-info/RECORD +44 -0
- jvcli-2.0.1.dist-info/RECORD +0 -32
- /jvcli/{client → studio}/assets/index-BtFItD2q.js +0 -0
- /jvcli/{client → studio}/assets/index-CIEsu-TC.css +0 -0
- /jvcli/{client → studio}/index.html +0 -0
- /jvcli/{client → studio}/jac_logo.png +0 -0
- /jvcli/{client → studio}/tauri.svg +0 -0
- /jvcli/{client → studio}/vite.svg +0 -0
- {jvcli-2.0.1.dist-info → jvcli-2.0.3.dist-info}/LICENSE +0 -0
- {jvcli-2.0.1.dist-info → jvcli-2.0.3.dist-info}/WHEEL +0 -0
- {jvcli-2.0.1.dist-info → jvcli-2.0.3.dist-info}/entry_points.txt +0 -0
- {jvcli-2.0.1.dist-info → jvcli-2.0.3.dist-info}/top_level.txt +0 -0
jvcli/__init__.py
CHANGED
jvcli/cli.py
CHANGED
@@ -4,6 +4,7 @@ import click
|
|
4
4
|
|
5
5
|
from jvcli import __version__
|
6
6
|
from jvcli.commands.auth import login, logout, signup
|
7
|
+
from jvcli.commands.client import client
|
7
8
|
from jvcli.commands.create import create
|
8
9
|
from jvcli.commands.download import download
|
9
10
|
from jvcli.commands.info import info
|
@@ -26,6 +27,7 @@ jvcli.add_command(download)
|
|
26
27
|
jvcli.add_command(publish)
|
27
28
|
jvcli.add_command(info)
|
28
29
|
jvcli.add_command(studio)
|
30
|
+
jvcli.add_command(client)
|
29
31
|
|
30
32
|
# Register standalone commands
|
31
33
|
jvcli.add_command(signup)
|
jvcli/client/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
"""This package contains the client-side code for the JVCLI client streamlit app."""
|
jvcli/client/app.py
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
"""This module contains the main application logic for the JVCLI client."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
|
5
|
+
import requests
|
6
|
+
import streamlit as st
|
7
|
+
from streamlit_router import StreamlitRouter
|
8
|
+
|
9
|
+
from jvcli.client.lib.page import Page
|
10
|
+
from jvcli.client.lib.utils import call_list_actions, call_list_agents, load_function
|
11
|
+
from jvcli.client.pages import analytics_page, chat_page, dashboard_page, graph_page
|
12
|
+
|
13
|
+
JIVAS_URL = os.environ.get("JIVAS_URL", "http://localhost:8000")
|
14
|
+
|
15
|
+
JIVAS_STUDIO_URL = os.environ.get("JIVAS_STUDIO_URL", "http://localhost:8989")
|
16
|
+
|
17
|
+
|
18
|
+
def handle_agent_selection() -> None:
|
19
|
+
"""Handle the selection of an agent."""
|
20
|
+
if "selected_agent" in st.session_state:
|
21
|
+
st.query_params["agent"] = st.session_state.selected_agent["id"]
|
22
|
+
st.session_state.messages = {}
|
23
|
+
|
24
|
+
|
25
|
+
def login_form() -> None:
|
26
|
+
"""Render the login form and handle login logic."""
|
27
|
+
login_url = f"{JIVAS_URL}/user/login"
|
28
|
+
|
29
|
+
with st.container(border=True):
|
30
|
+
st.header("Login")
|
31
|
+
|
32
|
+
email = st.text_input("Email")
|
33
|
+
password = st.text_input("Password", type="password")
|
34
|
+
|
35
|
+
if st.button("Login"):
|
36
|
+
response = requests.post(
|
37
|
+
login_url, json={"email": email, "password": password}
|
38
|
+
)
|
39
|
+
|
40
|
+
if response.status_code == 200:
|
41
|
+
st.session_state.ROOT_ID = response.json()["user"]["root_id"]
|
42
|
+
st.session_state.TOKEN = response.json()["token"]
|
43
|
+
st.session_state.EXPIRATION = response.json()["user"]["expiration"]
|
44
|
+
st.rerun()
|
45
|
+
|
46
|
+
|
47
|
+
def main() -> None:
|
48
|
+
"""Main function to render the Streamlit app."""
|
49
|
+
hide_sidebar = st.query_params.get("hide_sidebar")
|
50
|
+
router = StreamlitRouter()
|
51
|
+
|
52
|
+
# Initialize session state
|
53
|
+
for key in [
|
54
|
+
"messages",
|
55
|
+
"session_id",
|
56
|
+
"EXPIRATION",
|
57
|
+
"agents",
|
58
|
+
"actions_data",
|
59
|
+
"TOKEN",
|
60
|
+
"ROOT_ID",
|
61
|
+
"EXPIRATION",
|
62
|
+
]:
|
63
|
+
if key not in st.session_state:
|
64
|
+
if key == "messages":
|
65
|
+
st.session_state[key] = {}
|
66
|
+
else:
|
67
|
+
st.session_state[key] = [] if key in ["actions_data"] else ""
|
68
|
+
|
69
|
+
if hide_sidebar == "true":
|
70
|
+
st.markdown(
|
71
|
+
"""
|
72
|
+
<style>
|
73
|
+
[data-testid="stSidebar"] {
|
74
|
+
display: none;
|
75
|
+
}
|
76
|
+
[data-testid="stSidebarCollapsedControl"] {
|
77
|
+
display: none;
|
78
|
+
}
|
79
|
+
</style>
|
80
|
+
""",
|
81
|
+
unsafe_allow_html=True,
|
82
|
+
)
|
83
|
+
|
84
|
+
# Setup the sidebar
|
85
|
+
with st.sidebar:
|
86
|
+
st.title("✧ JIVAS Manager")
|
87
|
+
# retrieve agent list
|
88
|
+
agents = call_list_agents()
|
89
|
+
|
90
|
+
try:
|
91
|
+
selected_agent_id = st.query_params["agent"]
|
92
|
+
except KeyError:
|
93
|
+
st.query_params["agent"] = agents[0]["id"] if agents else None
|
94
|
+
selected_agent_id = st.query_params["agent"]
|
95
|
+
|
96
|
+
selected_agent_index = next(
|
97
|
+
(i for i, item in enumerate(agents) if item["id"] == selected_agent_id),
|
98
|
+
len(agents) - 1 if agents else None,
|
99
|
+
)
|
100
|
+
|
101
|
+
# Render the ComboBox using streamlit-elements
|
102
|
+
st.sidebar.selectbox(
|
103
|
+
"Agent",
|
104
|
+
agents,
|
105
|
+
index=selected_agent_index,
|
106
|
+
placeholder="Select agent...",
|
107
|
+
format_func=lambda x: x["label"] if "label" in x else x,
|
108
|
+
on_change=handle_agent_selection,
|
109
|
+
key="selected_agent",
|
110
|
+
)
|
111
|
+
|
112
|
+
# Expander for the menu
|
113
|
+
with st.expander("Menu", True):
|
114
|
+
Page(router).item(analytics_page.render, "Dashboard", "/").st_button()
|
115
|
+
Page(router).item(chat_page.render, "Chat", "/chat").st_button()
|
116
|
+
Page(router).item(dashboard_page.render, "Actions", "/actions").st_button()
|
117
|
+
Page(router).item(graph_page.render, "Graph", "/graph").st_button()
|
118
|
+
st.button(
|
119
|
+
"Logout", on_click=dashboard_page.logout, use_container_width=True
|
120
|
+
)
|
121
|
+
|
122
|
+
with st.expander("Action Apps", False):
|
123
|
+
if selected_agent_id and (
|
124
|
+
actions_data := call_list_actions(agent_id=selected_agent_id)
|
125
|
+
):
|
126
|
+
st.session_state.actions_data = actions_data
|
127
|
+
|
128
|
+
for action in actions_data:
|
129
|
+
package = action.get("_package", {})
|
130
|
+
|
131
|
+
if package.get("config", {}).get("app", False):
|
132
|
+
func = load_function(
|
133
|
+
f"{package['config']['path']}/app/app.py",
|
134
|
+
"render",
|
135
|
+
router=router,
|
136
|
+
agent_id=selected_agent_id,
|
137
|
+
action_id=action["id"],
|
138
|
+
info=package,
|
139
|
+
)
|
140
|
+
# register the route to the app
|
141
|
+
Page(router).item(
|
142
|
+
callback=func,
|
143
|
+
label=package["meta"]["title"],
|
144
|
+
path=f'/{Page.normalize_label(package["meta"]["title"])}',
|
145
|
+
).st_button()
|
146
|
+
router.serve()
|
147
|
+
|
148
|
+
|
149
|
+
# Initialize Streamlit app config
|
150
|
+
if __name__ == "__main__":
|
151
|
+
token_query = st.query_params.get("token")
|
152
|
+
if token_query:
|
153
|
+
st.session_state.TOKEN = token_query
|
154
|
+
|
155
|
+
if "TOKEN" not in st.session_state:
|
156
|
+
st.set_page_config(page_title="JIVAS Manager", page_icon="💠")
|
157
|
+
login_form()
|
158
|
+
else:
|
159
|
+
st.set_page_config(page_title="JIVAS Manager", page_icon="💠", layout="wide")
|
160
|
+
main()
|
@@ -0,0 +1 @@
|
|
1
|
+
"""jvcli client library functions."""
|
jvcli/client/lib/page.py
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
"""This module contains the Page class used for managing pages in the JVCLI client."""
|
2
|
+
|
3
|
+
from typing import Callable, Dict, Optional
|
4
|
+
|
5
|
+
import streamlit as st
|
6
|
+
from streamlit_router import StreamlitRouter
|
7
|
+
|
8
|
+
|
9
|
+
class Page:
|
10
|
+
"""Class to manage pages in the JVCLI client."""
|
11
|
+
|
12
|
+
def __init__(self, router: StreamlitRouter) -> None:
|
13
|
+
"""Initialize the Page with a router."""
|
14
|
+
self._router: StreamlitRouter = router
|
15
|
+
self._callback: Optional[Callable] = None
|
16
|
+
self._label: Optional[str] = None
|
17
|
+
self._path: Optional[str] = None
|
18
|
+
self._key: Optional[str] = None
|
19
|
+
self._args: Optional[Dict] = None
|
20
|
+
|
21
|
+
def item(
|
22
|
+
self, callback: Callable, label: str, path: str, args: Optional[Dict] = None
|
23
|
+
) -> "Page":
|
24
|
+
"""
|
25
|
+
Register the page callable on the given route.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
callback (Callable): The function to call for the page.
|
29
|
+
label (str): The label for the page.
|
30
|
+
path (str): The path for the page.
|
31
|
+
args (Optional[Dict], optional): Additional arguments for the page. Defaults to None.
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Page: The current Page instance.
|
35
|
+
"""
|
36
|
+
if args is None:
|
37
|
+
args = {}
|
38
|
+
self._callback = callback
|
39
|
+
self._label = label
|
40
|
+
self._path = path
|
41
|
+
self._args = args
|
42
|
+
self._key = f"{Page.normalize_label(label)}"
|
43
|
+
self._router.register(func=self._callback, path=self._path, endpoint=self._key)
|
44
|
+
return self
|
45
|
+
|
46
|
+
def st_button(self) -> None:
|
47
|
+
"""Add the Streamlit link for this page wherever it is called."""
|
48
|
+
if st.button(self._label, key=self._key, use_container_width=True):
|
49
|
+
self._router.redirect(*self._router.build(self._key, self._args))
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def normalize_label(label: str) -> str:
|
53
|
+
"""
|
54
|
+
Normalize the label to be used as a key.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
label (str): The label to normalize.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
str: The normalized label.
|
61
|
+
"""
|
62
|
+
return (
|
63
|
+
"".join(char.lower() for char in label if char.isascii())
|
64
|
+
.strip()
|
65
|
+
.replace(" ", "-")
|
66
|
+
.replace("/", "-")
|
67
|
+
.replace(":", "-")
|
68
|
+
)
|
@@ -0,0 +1,334 @@
|
|
1
|
+
"""This module contains utility functions for the JVCLI client."""
|
2
|
+
|
3
|
+
import base64
|
4
|
+
import json
|
5
|
+
import os
|
6
|
+
from importlib.util import module_from_spec, spec_from_file_location
|
7
|
+
from io import BytesIO
|
8
|
+
from typing import Any, Callable, Dict, List, Optional
|
9
|
+
|
10
|
+
import requests
|
11
|
+
import streamlit as st
|
12
|
+
import yaml
|
13
|
+
from PIL import Image
|
14
|
+
|
15
|
+
JIVAS_URL = os.environ.get("JIVAS_URL", "http://localhost:8000")
|
16
|
+
|
17
|
+
|
18
|
+
def load_function(file_path: str, function_name: str, **kwargs: Any) -> Callable:
|
19
|
+
"""Dynamically loads and returns a function from a Python file, with optional keyword arguments."""
|
20
|
+
|
21
|
+
if not os.path.exists(file_path):
|
22
|
+
raise FileNotFoundError(f"No file found at {file_path}")
|
23
|
+
|
24
|
+
# Get the module name from the file name
|
25
|
+
module_name = os.path.splitext(os.path.basename(file_path))[0]
|
26
|
+
|
27
|
+
# Load the module specification
|
28
|
+
spec = spec_from_file_location(module_name, file_path)
|
29
|
+
if spec is None:
|
30
|
+
raise ImportError(f"Could not load specification for module {module_name}")
|
31
|
+
|
32
|
+
# Create the module
|
33
|
+
module = module_from_spec(spec)
|
34
|
+
if spec.loader is None:
|
35
|
+
raise ImportError(f"Could not load module {module_name}")
|
36
|
+
|
37
|
+
# Execute the module
|
38
|
+
spec.loader.exec_module(module)
|
39
|
+
|
40
|
+
# Get the function
|
41
|
+
if not hasattr(module, function_name):
|
42
|
+
raise AttributeError(f"Function '{function_name}' not found in {file_path}")
|
43
|
+
|
44
|
+
func = getattr(module, function_name)
|
45
|
+
|
46
|
+
# Ensure the returned callable can accept any kwargs passed to it
|
47
|
+
def wrapped_func(*args: Any, **func_kwargs: Any) -> Any:
|
48
|
+
return func(*args, **{**kwargs, **func_kwargs})
|
49
|
+
|
50
|
+
return wrapped_func
|
51
|
+
|
52
|
+
|
53
|
+
def call_list_agents() -> list:
|
54
|
+
"""Call the API to list agents."""
|
55
|
+
|
56
|
+
ctx = get_user_info()
|
57
|
+
|
58
|
+
endpoint = f"{JIVAS_URL}/walker/list_agents"
|
59
|
+
|
60
|
+
if ctx["token"]:
|
61
|
+
try:
|
62
|
+
headers = {"Authorization": "Bearer " + ctx["token"]}
|
63
|
+
json = {"reporting": True}
|
64
|
+
|
65
|
+
# call interact
|
66
|
+
response = requests.post(endpoint, json=json, headers=headers)
|
67
|
+
|
68
|
+
if response.status_code == 200:
|
69
|
+
result = response.json().get("reports", [])
|
70
|
+
if len(result) > 0:
|
71
|
+
return [
|
72
|
+
{"id": agent["id"], "label": agent["name"]} for agent in result
|
73
|
+
]
|
74
|
+
|
75
|
+
if response.status_code == 401:
|
76
|
+
st.session_state.EXPIRATION = ""
|
77
|
+
return []
|
78
|
+
|
79
|
+
except Exception as e:
|
80
|
+
st.session_state.EXPIRATION = ""
|
81
|
+
print("Exception occurred: ", e)
|
82
|
+
|
83
|
+
return []
|
84
|
+
|
85
|
+
|
86
|
+
def call_list_actions(agent_id: str) -> list:
|
87
|
+
"""Call the API to list actions for a given agent."""
|
88
|
+
|
89
|
+
ctx = get_user_info()
|
90
|
+
|
91
|
+
endpoint = f"{JIVAS_URL}/walker/list_actions"
|
92
|
+
|
93
|
+
if ctx["token"]:
|
94
|
+
try:
|
95
|
+
headers = {"Authorization": "Bearer " + ctx["token"]}
|
96
|
+
json = {"agent_id": agent_id}
|
97
|
+
|
98
|
+
# call interact
|
99
|
+
response = requests.post(endpoint, json=json, headers=headers)
|
100
|
+
|
101
|
+
if response.status_code == 200:
|
102
|
+
result = (response.json()).get("reports", [])
|
103
|
+
if len(result) > 0:
|
104
|
+
return result[0]
|
105
|
+
else:
|
106
|
+
return []
|
107
|
+
|
108
|
+
if response.status_code == 401:
|
109
|
+
st.session_state.EXPIRATION = ""
|
110
|
+
return []
|
111
|
+
|
112
|
+
except Exception as e:
|
113
|
+
st.session_state.EXPIRATION = ""
|
114
|
+
print("Exception occurred: ", e)
|
115
|
+
|
116
|
+
return []
|
117
|
+
|
118
|
+
|
119
|
+
def call_get_action(agent_id: str, action_id: str) -> list:
|
120
|
+
"""Call the API to get a specific action for a given agent."""
|
121
|
+
|
122
|
+
ctx = get_user_info()
|
123
|
+
|
124
|
+
endpoint = f"{JIVAS_URL}/walker/get_action"
|
125
|
+
|
126
|
+
if ctx["token"]:
|
127
|
+
try:
|
128
|
+
headers = {"Authorization": "Bearer " + ctx["token"]}
|
129
|
+
json = {"agent_id": agent_id, "action_id": action_id}
|
130
|
+
|
131
|
+
# call interact
|
132
|
+
response = requests.post(endpoint, json=json, headers=headers)
|
133
|
+
|
134
|
+
if response.status_code == 200:
|
135
|
+
result = (response.json()).get("reports", [])
|
136
|
+
if len(result) > 0:
|
137
|
+
return result[0]
|
138
|
+
else:
|
139
|
+
return []
|
140
|
+
|
141
|
+
if response.status_code == 401:
|
142
|
+
st.session_state.EXPIRATION = ""
|
143
|
+
return []
|
144
|
+
|
145
|
+
except Exception as e:
|
146
|
+
st.session_state.EXPIRATION = ""
|
147
|
+
print("Exception occurred: ", e)
|
148
|
+
|
149
|
+
return []
|
150
|
+
|
151
|
+
|
152
|
+
def call_update_action(agent_id: str, action_id: str, action_data: dict) -> dict:
|
153
|
+
"""Call the API to update a specific action for a given agent."""
|
154
|
+
|
155
|
+
ctx = get_user_info()
|
156
|
+
|
157
|
+
endpoint = f"{JIVAS_URL}/walker/update_action"
|
158
|
+
|
159
|
+
if ctx["token"]:
|
160
|
+
try:
|
161
|
+
headers = {"Authorization": "Bearer " + ctx["token"]}
|
162
|
+
json = {
|
163
|
+
"agent_id": agent_id,
|
164
|
+
"action_id": action_id,
|
165
|
+
"action_data": action_data,
|
166
|
+
}
|
167
|
+
|
168
|
+
# call interact
|
169
|
+
response = requests.post(endpoint, json=json, headers=headers)
|
170
|
+
|
171
|
+
if response.status_code == 200:
|
172
|
+
result = (response.json()).get("reports", [])
|
173
|
+
if len(result) > 0:
|
174
|
+
return result[0]
|
175
|
+
else:
|
176
|
+
return {}
|
177
|
+
|
178
|
+
if response.status_code == 401:
|
179
|
+
st.session_state.EXPIRATION = ""
|
180
|
+
return {}
|
181
|
+
|
182
|
+
except Exception as e:
|
183
|
+
st.session_state.EXPIRATION = ""
|
184
|
+
print("Exception occurred: ", e)
|
185
|
+
|
186
|
+
return {}
|
187
|
+
|
188
|
+
|
189
|
+
def call_action_walker_exec(
|
190
|
+
agent_id: str,
|
191
|
+
module_root: str,
|
192
|
+
walker: str,
|
193
|
+
args: Optional[Dict] = None,
|
194
|
+
files: Optional[List] = None,
|
195
|
+
headers: Optional[Dict] = None,
|
196
|
+
) -> list:
|
197
|
+
"""Call the API to execute a walker action for a given agent."""
|
198
|
+
|
199
|
+
ctx = get_user_info()
|
200
|
+
|
201
|
+
endpoint = f"{JIVAS_URL}/walker/action/walker"
|
202
|
+
|
203
|
+
if ctx.get("token"):
|
204
|
+
try:
|
205
|
+
headers = headers if headers else {}
|
206
|
+
headers["Authorization"] = "Bearer " + ctx["token"]
|
207
|
+
|
208
|
+
# Create form data
|
209
|
+
data = {"agent_id": agent_id, "module_root": module_root, "walker": walker}
|
210
|
+
|
211
|
+
if args:
|
212
|
+
data["args"] = json.dumps(args)
|
213
|
+
|
214
|
+
if files:
|
215
|
+
file_list = []
|
216
|
+
|
217
|
+
for file in files:
|
218
|
+
file_list.append(("attachments", (file[0], file[1], file[2])))
|
219
|
+
|
220
|
+
# Dispatch request
|
221
|
+
response = requests.post(
|
222
|
+
url=endpoint, headers=headers, data=data, files=file_list
|
223
|
+
)
|
224
|
+
|
225
|
+
if response.status_code == 200:
|
226
|
+
result = response.json()
|
227
|
+
return result if result else []
|
228
|
+
|
229
|
+
if response.status_code == 401:
|
230
|
+
st.session_state.EXPIRATION = ""
|
231
|
+
return []
|
232
|
+
|
233
|
+
except Exception as e:
|
234
|
+
st.session_state.EXPIRATION = ""
|
235
|
+
st.write(e)
|
236
|
+
|
237
|
+
return []
|
238
|
+
|
239
|
+
|
240
|
+
def call_import_agent(
|
241
|
+
descriptor: str,
|
242
|
+
headers: Optional[Dict] = None,
|
243
|
+
) -> list:
|
244
|
+
"""Call the API to import an agent."""
|
245
|
+
|
246
|
+
ctx = get_user_info()
|
247
|
+
|
248
|
+
endpoint = f"{JIVAS_URL}/walker/import_agent"
|
249
|
+
|
250
|
+
if ctx.get("token"):
|
251
|
+
try:
|
252
|
+
headers = headers if headers else {}
|
253
|
+
headers["Authorization"] = "Bearer " + ctx["token"]
|
254
|
+
headers["Content-Type"] = "application/json"
|
255
|
+
headers["Accept"] = "application/json"
|
256
|
+
|
257
|
+
data = {"descriptor": descriptor}
|
258
|
+
|
259
|
+
# Dispatch request
|
260
|
+
response = requests.post(endpoint, headers=headers, json=data)
|
261
|
+
|
262
|
+
if response.status_code == 200:
|
263
|
+
result = response.json()
|
264
|
+
return result if result else []
|
265
|
+
|
266
|
+
if response.status_code == 401:
|
267
|
+
st.session_state.EXPIRATION = ""
|
268
|
+
return []
|
269
|
+
|
270
|
+
except Exception as e:
|
271
|
+
st.session_state.EXPIRATION = ""
|
272
|
+
st.write(e)
|
273
|
+
|
274
|
+
return []
|
275
|
+
|
276
|
+
|
277
|
+
def get_user_info() -> dict:
|
278
|
+
"""Get user information from the session state."""
|
279
|
+
return {
|
280
|
+
"root_id": st.session_state.ROOT_ID,
|
281
|
+
"token": st.session_state.TOKEN,
|
282
|
+
"expiration": st.session_state.EXPIRATION,
|
283
|
+
}
|
284
|
+
|
285
|
+
|
286
|
+
def decode_base64_image(base64_string: str) -> Image:
|
287
|
+
"""Decode a base64 string into an image."""
|
288
|
+
# Decode the base64 string
|
289
|
+
image_data = base64.b64decode(base64_string)
|
290
|
+
|
291
|
+
# Create a bytes buffer from the decoded bytes
|
292
|
+
image_buffer = BytesIO(image_data)
|
293
|
+
|
294
|
+
# Open the image using PIL
|
295
|
+
return Image.open(image_buffer)
|
296
|
+
|
297
|
+
|
298
|
+
class LongStringDumper(yaml.SafeDumper):
|
299
|
+
"""Custom YAML dumper to handle long strings."""
|
300
|
+
|
301
|
+
def represent_scalar(
|
302
|
+
self, tag: str, value: str, style: Optional[str] = None
|
303
|
+
) -> yaml.ScalarNode:
|
304
|
+
"""Represent scalar values, using block style for long strings."""
|
305
|
+
# Replace any escape sequences to format the output as desired
|
306
|
+
if (
|
307
|
+
len(value) > 150 or "\n" in value
|
308
|
+
): # Adjust the threshold for long strings as needed
|
309
|
+
style = "|"
|
310
|
+
# converts all newline escapes to actual representations
|
311
|
+
value = "\n".join([line.rstrip() for line in value.split("\n")])
|
312
|
+
else:
|
313
|
+
# converts all newline escapes to actual representations
|
314
|
+
value = "\n".join([line.rstrip() for line in value.split("\n")]).rstrip()
|
315
|
+
|
316
|
+
return super().represent_scalar(tag, value, style)
|
317
|
+
|
318
|
+
|
319
|
+
def jac_yaml_dumper(
|
320
|
+
data: Any,
|
321
|
+
indent: int = 2,
|
322
|
+
default_flow_style: bool = False,
|
323
|
+
allow_unicode: bool = True,
|
324
|
+
sort_keys: bool = False,
|
325
|
+
) -> str:
|
326
|
+
"""Dumps YAML data using LongStringDumper with customizable options."""
|
327
|
+
return yaml.dump(
|
328
|
+
data,
|
329
|
+
Dumper=LongStringDumper,
|
330
|
+
indent=indent,
|
331
|
+
default_flow_style=default_flow_style,
|
332
|
+
allow_unicode=allow_unicode,
|
333
|
+
sort_keys=sort_keys,
|
334
|
+
)
|