jvcli 2.0.16__tar.gz → 2.0.17__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.
- {jvcli-2.0.16/jvcli.egg-info → jvcli-2.0.17}/PKG-INFO +1 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/__init__.py +1 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/app.py +15 -8
- jvcli-2.0.17/jvcli/client/lib/utils.py +310 -0
- jvcli-2.0.16/jvcli/client/pages/dashboard_page.py → jvcli-2.0.17/jvcli/client/pages/action_dashboard_page.py +5 -4
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/pages/analytics_page.py +73 -7
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/pages/chat_page.py +3 -3
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/client.py +2 -2
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/create.py +1 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/download.py +1 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/daf/README.md +1 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/env.example +5 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/gitignore.example +2 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/initagents.sh +3 -0
- jvcli-2.0.17/jvcli/templates/2.0.0/project/sh/jacclean.sh +11 -0
- {jvcli-2.0.16 → jvcli-2.0.17/jvcli.egg-info}/PKG-INFO +1 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli.egg-info/SOURCES.txt +3 -1
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_create.py +6 -6
- jvcli-2.0.17/tests/test_render_basic.py +23 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_startproject.py +1 -0
- jvcli-2.0.17/tests/test_utils.py +348 -0
- jvcli-2.0.16/jvcli/client/lib/utils.py +0 -335
- jvcli-2.0.16/tests/test_utils.py +0 -421
- {jvcli-2.0.16 → jvcli-2.0.17}/LICENSE +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/MANIFEST.in +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/README.md +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/api.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/auth.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/cli.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/__init__.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/lib/__init__.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/lib/page.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/lib/widgets.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/pages/__init__.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/client/pages/graph_page.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/__init__.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/auth.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/info.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/publish.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/startproject.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/studio.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/commands/update.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio/assets/index-DDV79SDu.js +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio/assets/index-DdMMONxd.css +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio/index.html +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio/jac_logo.png +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio/tauri.svg +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio/vite.svg +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio-auth/assets/index-Bh6lyeXA.js +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio-auth/assets/index-DdMMONxd.css +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio-auth/index.html +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio-auth/jac_logo.png +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio-auth/tauri.svg +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/studio-auth/vite.svg +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/action_info.yaml +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_descriptor.yaml +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_info.yaml +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_knowledge.yaml +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_memory.yaml +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/README.md +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/actions/README.md +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/globals.jac +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/main.jac +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/exportenv.sh +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/importagent.sh +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/inituser.sh +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/serve.sh +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/startclient.sh +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/tests/README.md +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/CHANGELOG.md +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/templates/README.md +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli/utils.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli.egg-info/dependency_links.txt +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli.egg-info/entry_points.txt +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli.egg-info/requires.txt +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/jvcli.egg-info/top_level.txt +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/setup.cfg +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/setup.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_api.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_auth.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_cli.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_download.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_info.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_publish.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_studio.py +0 -0
- {jvcli-2.0.16 → jvcli-2.0.17}/tests/test_update.py +0 -0
@@ -8,10 +8,14 @@ from streamlit_router import StreamlitRouter
|
|
8
8
|
|
9
9
|
from jvcli.client.lib.page import Page
|
10
10
|
from jvcli.client.lib.utils import call_list_actions, call_list_agents, load_function
|
11
|
-
from jvcli.client.pages import
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
from jvcli.client.pages import (
|
12
|
+
action_dashboard_page,
|
13
|
+
analytics_page,
|
14
|
+
chat_page,
|
15
|
+
graph_page,
|
16
|
+
)
|
17
|
+
|
18
|
+
JIVAS_BASE_URL = os.environ.get("JIVAS_BASE_URL", "http://localhost:8000")
|
15
19
|
JIVAS_STUDIO_URL = os.environ.get("JIVAS_STUDIO_URL", "http://localhost:8989")
|
16
20
|
|
17
21
|
|
@@ -24,7 +28,7 @@ def handle_agent_selection() -> None:
|
|
24
28
|
|
25
29
|
def login_form() -> None:
|
26
30
|
"""Render the login form and handle login logic."""
|
27
|
-
login_url = f"{
|
31
|
+
login_url = f"{JIVAS_BASE_URL}/user/login"
|
28
32
|
|
29
33
|
if os.environ.get("JIVAS_ENVIRONMENT") == "development":
|
30
34
|
email = os.environ.get("JIVAS_USER", "admin@jivas.com")
|
@@ -67,7 +71,6 @@ def main() -> None:
|
|
67
71
|
for key in [
|
68
72
|
"messages",
|
69
73
|
"session_id",
|
70
|
-
"EXPIRATION",
|
71
74
|
"agents",
|
72
75
|
"actions_data",
|
73
76
|
"TOKEN",
|
@@ -127,10 +130,14 @@ def main() -> None:
|
|
127
130
|
with st.expander("Menu", True):
|
128
131
|
Page(router).item(analytics_page.render, "Dashboard", "/").st_button()
|
129
132
|
Page(router).item(chat_page.render, "Chat", "/chat").st_button()
|
130
|
-
Page(router).item(
|
133
|
+
Page(router).item(
|
134
|
+
action_dashboard_page.render, "Actions", "/actions"
|
135
|
+
).st_button()
|
131
136
|
Page(router).item(graph_page.render, "Graph", "/graph").st_button()
|
132
137
|
st.button(
|
133
|
-
"Logout",
|
138
|
+
"Logout",
|
139
|
+
on_click=action_dashboard_page.logout,
|
140
|
+
use_container_width=True,
|
134
141
|
)
|
135
142
|
|
136
143
|
with st.expander("Action Apps", False):
|
@@ -0,0 +1,310 @@
|
|
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_BASE_URL = os.environ.get("JIVAS_BASE_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_api(
|
54
|
+
endpoint: str,
|
55
|
+
method: str = "POST",
|
56
|
+
headers: Optional[Dict] = None,
|
57
|
+
json_data: Optional[Dict] = None,
|
58
|
+
files: Optional[List] = None,
|
59
|
+
data: Optional[Dict] = None,
|
60
|
+
) -> Optional[requests.Response]:
|
61
|
+
"""Generic function to call an API endpoint."""
|
62
|
+
|
63
|
+
if not endpoint.startswith("http"):
|
64
|
+
endpoint = f"{JIVAS_BASE_URL}/{endpoint}"
|
65
|
+
|
66
|
+
ctx = get_user_info() # Assumes a function that fetches user info
|
67
|
+
|
68
|
+
if ctx.get("token"):
|
69
|
+
try:
|
70
|
+
headers = headers or {}
|
71
|
+
headers["Authorization"] = f"Bearer {ctx['token']}"
|
72
|
+
|
73
|
+
response = requests.request(
|
74
|
+
method=method,
|
75
|
+
url=endpoint,
|
76
|
+
headers=headers,
|
77
|
+
json=json_data,
|
78
|
+
files=files,
|
79
|
+
data=data,
|
80
|
+
)
|
81
|
+
|
82
|
+
if response.status_code == 401:
|
83
|
+
st.session_state.EXPIRATION = ""
|
84
|
+
return None
|
85
|
+
|
86
|
+
return response
|
87
|
+
|
88
|
+
except Exception as e:
|
89
|
+
st.session_state.EXPIRATION = ""
|
90
|
+
st.write(e)
|
91
|
+
|
92
|
+
return None
|
93
|
+
|
94
|
+
|
95
|
+
def call_action_walker_exec(
|
96
|
+
agent_id: str,
|
97
|
+
module_root: str,
|
98
|
+
walker: str,
|
99
|
+
args: Optional[Dict] = None,
|
100
|
+
files: Optional[List] = None,
|
101
|
+
headers: Optional[Dict] = None,
|
102
|
+
) -> list:
|
103
|
+
"""Call the API to execute a walker action for a given agent."""
|
104
|
+
|
105
|
+
endpoint = f"{JIVAS_BASE_URL}/action/walker"
|
106
|
+
|
107
|
+
# Create form data
|
108
|
+
data = {"agent_id": agent_id, "module_root": module_root, "walker": walker}
|
109
|
+
|
110
|
+
if args:
|
111
|
+
data["args"] = json.dumps(args)
|
112
|
+
|
113
|
+
file_list = []
|
114
|
+
|
115
|
+
if files:
|
116
|
+
for file in files:
|
117
|
+
file_list.append(("attachments", (file[0], file[1], file[2])))
|
118
|
+
|
119
|
+
response = call_api(endpoint, headers=headers, data=data, files=file_list)
|
120
|
+
|
121
|
+
if response and response.status_code == 200:
|
122
|
+
result = response.json()
|
123
|
+
return result if result else []
|
124
|
+
|
125
|
+
return []
|
126
|
+
|
127
|
+
|
128
|
+
def call_healthcheck(agent_id: str, headers: Optional[Dict] = None) -> Optional[dict]:
|
129
|
+
"""Call the API to check the health of an agent."""
|
130
|
+
endpoint = "walker/healthcheck"
|
131
|
+
json_data = {"agent_id": agent_id}
|
132
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
133
|
+
|
134
|
+
if response and response.status_code in [200, 501, 503]:
|
135
|
+
result = response.json()
|
136
|
+
reports = result.get("reports", [])
|
137
|
+
return reports[0] if reports else {}
|
138
|
+
|
139
|
+
return {}
|
140
|
+
|
141
|
+
|
142
|
+
def call_list_agents(headers: Optional[Dict] = None) -> list:
|
143
|
+
"""Call the API to list agents."""
|
144
|
+
endpoint = "walker/list_agents"
|
145
|
+
json_data = {"reporting": True}
|
146
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
147
|
+
|
148
|
+
if response and response.status_code == 200:
|
149
|
+
result = response.json()
|
150
|
+
reports = result.get("reports", [])
|
151
|
+
return [
|
152
|
+
{"id": agent.get("id", ""), "label": agent.get("name", "")}
|
153
|
+
for agent in reports
|
154
|
+
]
|
155
|
+
|
156
|
+
return []
|
157
|
+
|
158
|
+
|
159
|
+
def call_get_agent(agent_id: str, headers: Optional[Dict] = None) -> dict:
|
160
|
+
"""Call the API to get details of a specific agent."""
|
161
|
+
endpoint = "walker/get_agent"
|
162
|
+
json_data = {"agent_id": agent_id}
|
163
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
164
|
+
|
165
|
+
if response and response.status_code == 200:
|
166
|
+
result = response.json()
|
167
|
+
reports = result.get("reports", [])
|
168
|
+
return reports[0] if reports else {}
|
169
|
+
|
170
|
+
return {}
|
171
|
+
|
172
|
+
|
173
|
+
def call_list_actions(agent_id: str, headers: Optional[Dict] = None) -> list:
|
174
|
+
"""Call the API to list actions for a given agent."""
|
175
|
+
endpoint = "walker/list_actions"
|
176
|
+
json_data = {"agent_id": agent_id}
|
177
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
178
|
+
|
179
|
+
if response and response.status_code == 200:
|
180
|
+
result = response.json()
|
181
|
+
reports = result.get("reports", [])
|
182
|
+
return reports[0] if reports else []
|
183
|
+
|
184
|
+
return []
|
185
|
+
|
186
|
+
|
187
|
+
def call_get_action(
|
188
|
+
agent_id: str, action_id: str, headers: Optional[Dict] = None
|
189
|
+
) -> dict:
|
190
|
+
"""Call the API to get a specific action for a given agent."""
|
191
|
+
endpoint = "walker/get_action"
|
192
|
+
json_data = {"agent_id": agent_id, "action_id": action_id}
|
193
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
194
|
+
|
195
|
+
if response and response.status_code == 200:
|
196
|
+
result = response.json()
|
197
|
+
reports = result.get("reports", [])
|
198
|
+
return reports[0] if reports else {}
|
199
|
+
|
200
|
+
return {}
|
201
|
+
|
202
|
+
|
203
|
+
def call_update_action(
|
204
|
+
agent_id: str, action_id: str, action_data: dict, headers: Optional[Dict] = None
|
205
|
+
) -> dict:
|
206
|
+
"""Call the API to update a specific action for a given agent."""
|
207
|
+
endpoint = "walker/update_action"
|
208
|
+
json_data = {
|
209
|
+
"agent_id": agent_id,
|
210
|
+
"action_id": action_id,
|
211
|
+
"action_data": action_data,
|
212
|
+
}
|
213
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
214
|
+
|
215
|
+
if response and response.status_code == 200:
|
216
|
+
result = response.json()
|
217
|
+
reports = result.get("reports", [])
|
218
|
+
return reports[0] if reports else {}
|
219
|
+
|
220
|
+
return {}
|
221
|
+
|
222
|
+
|
223
|
+
def call_update_agent(
|
224
|
+
agent_id: str, agent_data: dict, headers: Optional[Dict] = None
|
225
|
+
) -> dict:
|
226
|
+
"""Call the API to update a specific agent."""
|
227
|
+
endpoint = "walker/update_agent"
|
228
|
+
json_data = {"agent_id": agent_id, "agent_data": agent_data}
|
229
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
230
|
+
|
231
|
+
if response and response.status_code == 200:
|
232
|
+
result = response.json()
|
233
|
+
reports = result.get("reports", [])
|
234
|
+
return reports[0] if reports else {}
|
235
|
+
|
236
|
+
return {}
|
237
|
+
|
238
|
+
|
239
|
+
def call_import_agent(descriptor: str, headers: Optional[Dict] = None) -> list:
|
240
|
+
"""Call the API to import an agent."""
|
241
|
+
endpoint = "walker/import_agent"
|
242
|
+
json_data = {"descriptor": descriptor}
|
243
|
+
response = call_api(endpoint, headers=headers, json_data=json_data)
|
244
|
+
|
245
|
+
if response and response.status_code == 200:
|
246
|
+
result = response.json()
|
247
|
+
reports = result.get("reports", [])
|
248
|
+
return reports[0] if reports else []
|
249
|
+
|
250
|
+
return []
|
251
|
+
|
252
|
+
|
253
|
+
def get_user_info() -> dict:
|
254
|
+
"""Get user information from the session state."""
|
255
|
+
return {
|
256
|
+
"root_id": st.session_state.get("ROOT_ID", ""),
|
257
|
+
"token": st.session_state.get("TOKEN", ""),
|
258
|
+
"expiration": st.session_state.get("EXPIRATION", ""),
|
259
|
+
}
|
260
|
+
|
261
|
+
|
262
|
+
def decode_base64_image(base64_string: str) -> Image:
|
263
|
+
"""Decode a base64 string into an image."""
|
264
|
+
# Decode the base64 string
|
265
|
+
image_data = base64.b64decode(base64_string)
|
266
|
+
|
267
|
+
# Create a bytes buffer from the decoded bytes
|
268
|
+
image_buffer = BytesIO(image_data)
|
269
|
+
|
270
|
+
# Open the image using PIL
|
271
|
+
return Image.open(image_buffer)
|
272
|
+
|
273
|
+
|
274
|
+
class LongStringDumper(yaml.SafeDumper):
|
275
|
+
"""Custom YAML dumper to handle long strings."""
|
276
|
+
|
277
|
+
def represent_scalar(
|
278
|
+
self, tag: str, value: str, style: Optional[str] = None
|
279
|
+
) -> yaml.ScalarNode:
|
280
|
+
"""Represent scalar values, using block style for long strings."""
|
281
|
+
# Replace any escape sequences to format the output as desired
|
282
|
+
if (
|
283
|
+
len(value) > 150 or "\n" in value
|
284
|
+
): # Adjust the threshold for long strings as needed
|
285
|
+
style = "|"
|
286
|
+
# converts all newline escapes to actual representations
|
287
|
+
value = "\n".join([line.rstrip() for line in value.split("\n")])
|
288
|
+
else:
|
289
|
+
# converts all newline escapes to actual representations
|
290
|
+
value = "\n".join([line.rstrip() for line in value.split("\n")]).rstrip()
|
291
|
+
|
292
|
+
return super().represent_scalar(tag, value, style)
|
293
|
+
|
294
|
+
|
295
|
+
def jac_yaml_dumper(
|
296
|
+
data: Any,
|
297
|
+
indent: int = 2,
|
298
|
+
default_flow_style: bool = False,
|
299
|
+
allow_unicode: bool = True,
|
300
|
+
sort_keys: bool = False,
|
301
|
+
) -> str:
|
302
|
+
"""Dumps YAML data using LongStringDumper with customizable options."""
|
303
|
+
return yaml.dump(
|
304
|
+
data,
|
305
|
+
Dumper=LongStringDumper,
|
306
|
+
indent=indent,
|
307
|
+
default_flow_style=default_flow_style,
|
308
|
+
allow_unicode=allow_unicode,
|
309
|
+
sort_keys=sort_keys,
|
310
|
+
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
"""Render the
|
1
|
+
"""Render the action_dashboard page of the jvclient with actions data."""
|
2
2
|
|
3
3
|
import streamlit as st
|
4
4
|
from streamlit_elements import dashboard, elements, mui
|
@@ -10,7 +10,7 @@ from jvcli.client.lib.page import Page
|
|
10
10
|
def render(router: StreamlitRouter) -> None:
|
11
11
|
"""Render the dashboard page."""
|
12
12
|
if actions_data := st.session_state.get("actions_data"):
|
13
|
-
with elements("
|
13
|
+
with elements("action_dashboard"):
|
14
14
|
columns = 4
|
15
15
|
layout = []
|
16
16
|
|
@@ -20,7 +20,7 @@ def render(router: StreamlitRouter) -> None:
|
|
20
20
|
y = (idx // columns) * 2
|
21
21
|
width = 3
|
22
22
|
height = 2
|
23
|
-
# Add an item to the
|
23
|
+
# Add an item to the action_dashboard manually without using `with` if it's not a context manager
|
24
24
|
layout.append(
|
25
25
|
dashboard.Item(
|
26
26
|
f"card_{idx}",
|
@@ -36,10 +36,11 @@ def render(router: StreamlitRouter) -> None:
|
|
36
36
|
# now populate the actual cards with content
|
37
37
|
with dashboard.Grid(layout):
|
38
38
|
for idx, action in enumerate(actions_data):
|
39
|
+
|
39
40
|
package = action.get("_package", {})
|
40
41
|
title = package.get("meta", {}).get("title", action.get("label"))
|
41
42
|
description = action.get("description", "")
|
42
|
-
version =
|
43
|
+
version = package.get("version", "0.0.0")
|
43
44
|
action_type = package.get("meta", {}).get("type", "action")
|
44
45
|
key = Page.normalize_label(title)
|
45
46
|
enabled_color = "red"
|
@@ -3,6 +3,7 @@
|
|
3
3
|
import calendar
|
4
4
|
import datetime
|
5
5
|
import os
|
6
|
+
from typing import Optional
|
6
7
|
|
7
8
|
import pandas as pd
|
8
9
|
import requests
|
@@ -11,14 +12,79 @@ from streamlit.delta_generator import DeltaGenerator
|
|
11
12
|
from streamlit_javascript import st_javascript
|
12
13
|
from streamlit_router import StreamlitRouter
|
13
14
|
|
14
|
-
from jvcli.client.lib.utils import get_user_info
|
15
|
+
from jvcli.client.lib.utils import call_healthcheck, get_user_info
|
15
16
|
|
16
|
-
|
17
|
+
JIVAS_BASE_URL = os.environ.get("JIVAS_BASE_URL", "http://localhost:8000")
|
17
18
|
|
18
19
|
|
19
20
|
def render(router: StreamlitRouter) -> None:
|
20
21
|
"""Render the analytics page."""
|
21
|
-
|
22
|
+
|
23
|
+
selected_agent = st.session_state.get("selected_agent")
|
24
|
+
|
25
|
+
# Call the healthcheck endpoint and render the collapsible section
|
26
|
+
@st.cache_data(show_spinner=True)
|
27
|
+
def fetch_healthcheck(agent_id: str) -> Optional[dict]:
|
28
|
+
return call_healthcheck(agent_id)
|
29
|
+
|
30
|
+
health_data = None
|
31
|
+
|
32
|
+
if selected_agent:
|
33
|
+
# Clear the cache and fetch fresh data if the button is clicked
|
34
|
+
if st.session_state.get("recheck_health_clicked", False):
|
35
|
+
fetch_healthcheck.clear()
|
36
|
+
health_data = call_healthcheck(selected_agent["id"])
|
37
|
+
st.session_state["recheck_health_clicked"] = False
|
38
|
+
else:
|
39
|
+
# Use cached data
|
40
|
+
health_data = fetch_healthcheck(selected_agent["id"])
|
41
|
+
|
42
|
+
try:
|
43
|
+
if health_data:
|
44
|
+
trace = health_data.get("trace", {})
|
45
|
+
errors = [
|
46
|
+
f"{key}: {value['message']}"
|
47
|
+
for key, value in trace.items()
|
48
|
+
if value.get("severity") == "error"
|
49
|
+
]
|
50
|
+
warnings = [
|
51
|
+
f"{key}: {value['message']}"
|
52
|
+
for key, value in trace.items()
|
53
|
+
if value.get("severity") == "warning"
|
54
|
+
]
|
55
|
+
|
56
|
+
if errors:
|
57
|
+
section_label = "Agent health needs ATTENTION!"
|
58
|
+
section_color = "red"
|
59
|
+
expanded = True
|
60
|
+
elif warnings:
|
61
|
+
section_label = "Agent health is OK (with warnings)"
|
62
|
+
section_color = "orange"
|
63
|
+
expanded = True
|
64
|
+
else:
|
65
|
+
section_label = "Agent health is OK"
|
66
|
+
section_color = "green"
|
67
|
+
expanded = False
|
68
|
+
|
69
|
+
with st.expander(
|
70
|
+
f":{section_color}[{section_label}]", expanded=expanded
|
71
|
+
):
|
72
|
+
if errors:
|
73
|
+
st.error("Errors")
|
74
|
+
for error in errors:
|
75
|
+
st.text(f"- {error}")
|
76
|
+
if warnings:
|
77
|
+
st.warning("Warnings")
|
78
|
+
for warning in warnings:
|
79
|
+
st.text(f"- {warning}")
|
80
|
+
if st.button("Recheck Health", key="recheck_inside_expander"):
|
81
|
+
st.session_state["recheck_health_clicked"] = True
|
82
|
+
|
83
|
+
else:
|
84
|
+
st.error("Failed to fetch healthcheck data.")
|
85
|
+
except Exception as e:
|
86
|
+
st.error("An error occurred while fetching healthcheck data.")
|
87
|
+
print(e)
|
22
88
|
|
23
89
|
st.header("Analytics", divider=True)
|
24
90
|
today = datetime.date.today()
|
@@ -45,7 +111,7 @@ def render(router: StreamlitRouter) -> None:
|
|
45
111
|
)
|
46
112
|
|
47
113
|
try:
|
48
|
-
|
114
|
+
ctx = get_user_info()
|
49
115
|
if selected_agent and end_date > start_date:
|
50
116
|
interactions_chart(
|
51
117
|
token=ctx["token"],
|
@@ -87,7 +153,7 @@ def interactions_chart(
|
|
87
153
|
timezone: str,
|
88
154
|
) -> None:
|
89
155
|
"""Render the interactions chart."""
|
90
|
-
url = f"{
|
156
|
+
url = f"{JIVAS_BASE_URL}/walker/get_interactions_by_date"
|
91
157
|
|
92
158
|
with st.container(border=True):
|
93
159
|
st.subheader("Interactions by Date")
|
@@ -121,7 +187,7 @@ def users_chart(
|
|
121
187
|
timezone: str,
|
122
188
|
) -> None:
|
123
189
|
"""Render the users chart."""
|
124
|
-
url = f"{
|
190
|
+
url = f"{JIVAS_BASE_URL}/walker/get_users_by_date"
|
125
191
|
with st.container(border=True):
|
126
192
|
st.subheader("Users by Date")
|
127
193
|
response = requests.post(
|
@@ -154,7 +220,7 @@ def channels_chart(
|
|
154
220
|
timezone: str,
|
155
221
|
) -> None:
|
156
222
|
"""Render the channels chart."""
|
157
|
-
url = f"{
|
223
|
+
url = f"{JIVAS_BASE_URL}/walker/get_channels_by_date"
|
158
224
|
with st.container(border=True):
|
159
225
|
st.subheader("Channels by Date")
|
160
226
|
response = requests.post(
|
@@ -9,12 +9,12 @@ from streamlit_router import StreamlitRouter
|
|
9
9
|
|
10
10
|
from jvcli.client.lib.utils import get_user_info
|
11
11
|
|
12
|
-
|
12
|
+
JIVAS_BASE_URL = os.environ.get("JIVAS_BASE_URL", "http://localhost:8000")
|
13
13
|
|
14
14
|
|
15
15
|
def transcribe_audio(token: str, agent_id: str, file: bytes) -> dict:
|
16
16
|
"""Transcribe audio using the walker API."""
|
17
|
-
action_walker_url = f"{
|
17
|
+
action_walker_url = f"{JIVAS_BASE_URL}/action/walker"
|
18
18
|
|
19
19
|
# Create form data
|
20
20
|
files = {"attachments": ("audio.wav", file, "audio/wav")}
|
@@ -41,7 +41,7 @@ def transcribe_audio(token: str, agent_id: str, file: bytes) -> dict:
|
|
41
41
|
|
42
42
|
def render(router: StreamlitRouter) -> None:
|
43
43
|
"""Render the chat page."""
|
44
|
-
url = f"{
|
44
|
+
url = f"{JIVAS_BASE_URL}/interact"
|
45
45
|
ctx = get_user_info()
|
46
46
|
|
47
47
|
st.header("Chat", divider=True)
|
@@ -17,7 +17,7 @@ def client() -> None:
|
|
17
17
|
@click.option("--port", default=8501, help="Port for the client to launch on.")
|
18
18
|
@click.option(
|
19
19
|
"--jivas_url",
|
20
|
-
default=os.environ.get("
|
20
|
+
default=os.environ.get("JIVAS_BASE_URL", "http://localhost:8000"),
|
21
21
|
help="URL for the Jivas API.",
|
22
22
|
)
|
23
23
|
@click.option(
|
@@ -30,7 +30,7 @@ def launch(port: int, jivas_url: str, studio_url: str) -> None:
|
|
30
30
|
click.echo(
|
31
31
|
f"Launching Jivas Client on port {port}, loading action apps from {jivas_url}..."
|
32
32
|
)
|
33
|
-
os.environ["
|
33
|
+
os.environ["JIVAS_BASE_URL"] = jivas_url
|
34
34
|
os.environ["JIVAS_STUDIO_URL"] = studio_url
|
35
35
|
subprocess.call(
|
36
36
|
[
|
@@ -87,7 +87,7 @@ def _download_package(name: str, version: str, path: str, pkg_type: str) -> None
|
|
87
87
|
|
88
88
|
# checking for both daf and agent to maintain backward compatibility
|
89
89
|
if pkg_type == "agent" and package_type in ["agent", "daf"]:
|
90
|
-
base_dir = "
|
90
|
+
base_dir = "daf"
|
91
91
|
elif pkg_type == "action" and package_type.endswith("action"):
|
92
92
|
base_dir = "actions"
|
93
93
|
else:
|
@@ -21,7 +21,7 @@ To create a new DAF package using `jvcli`, follow these steps:
|
|
21
21
|
2. **Options**: You can customize the creation process with various options:
|
22
22
|
- `--version`: Specify the version of the agent. Default is `0.0.1`.
|
23
23
|
- `--jivas_version`: Specify the version of Jivas. Default is `2.0.0`.
|
24
|
-
- `--path`: Directory to create the agent folder in. Default is `./
|
24
|
+
- `--path`: Directory to create the agent folder in. Default is `./daf`.
|
25
25
|
- `--namespace`: Namespace for the agent. Defaults to the username in the token.
|
26
26
|
|
27
27
|
3. **Example**:
|
@@ -35,4 +35,8 @@ TOKEN_SECRET=s3cr3t
|
|
35
35
|
# TYPESENSE_PORT=8108
|
36
36
|
# TYPESENSE_PROTOCOL=http
|
37
37
|
# TYPESENSE_API_KEY=abcd
|
38
|
-
# TYPESENSE_CONNECTION_TIMEOUT_SECONDS=2
|
38
|
+
# TYPESENSE_CONNECTION_TIMEOUT_SECONDS=2
|
39
|
+
|
40
|
+
# WPP Config
|
41
|
+
# WPP_API_URL="http://localhost:21465"
|
42
|
+
# WPP_MASTER_KEY="wpp_secret"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# perform a jac_clean on the actions folder
|
4
|
+
# Navigate to the ./actions subdirectory and execute jac clean
|
5
|
+
if cd ./actions; then
|
6
|
+
jac clean
|
7
|
+
cd - > /dev/null
|
8
|
+
else
|
9
|
+
echo "Failed to navigate to ./actions directory. Exiting..."
|
10
|
+
exit 1
|
11
|
+
fi
|