jvcli 2.0.15__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.
Files changed (86) hide show
  1. {jvcli-2.0.15/jvcli.egg-info → jvcli-2.0.17}/PKG-INFO +2 -2
  2. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/__init__.py +1 -1
  3. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/app.py +15 -8
  4. jvcli-2.0.17/jvcli/client/lib/utils.py +310 -0
  5. jvcli-2.0.15/jvcli/client/pages/dashboard_page.py → jvcli-2.0.17/jvcli/client/pages/action_dashboard_page.py +5 -4
  6. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/pages/analytics_page.py +73 -7
  7. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/pages/chat_page.py +3 -3
  8. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/client.py +2 -2
  9. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/create.py +1 -1
  10. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/download.py +1 -1
  11. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/startproject.py +15 -8
  12. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/daf/README.md +1 -1
  13. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/env.example +6 -1
  14. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/gitignore.example +2 -1
  15. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/initagents.sh +3 -0
  16. jvcli-2.0.17/jvcli/templates/2.0.0/project/sh/jacclean.sh +11 -0
  17. {jvcli-2.0.15 → jvcli-2.0.17/jvcli.egg-info}/PKG-INFO +2 -2
  18. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli.egg-info/SOURCES.txt +3 -1
  19. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli.egg-info/requires.txt +1 -1
  20. {jvcli-2.0.15 → jvcli-2.0.17}/setup.py +1 -1
  21. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_create.py +6 -6
  22. jvcli-2.0.17/tests/test_render_basic.py +23 -0
  23. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_startproject.py +1 -0
  24. jvcli-2.0.17/tests/test_utils.py +348 -0
  25. jvcli-2.0.15/jvcli/client/lib/utils.py +0 -335
  26. jvcli-2.0.15/tests/test_utils.py +0 -421
  27. {jvcli-2.0.15 → jvcli-2.0.17}/LICENSE +0 -0
  28. {jvcli-2.0.15 → jvcli-2.0.17}/MANIFEST.in +0 -0
  29. {jvcli-2.0.15 → jvcli-2.0.17}/README.md +0 -0
  30. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/api.py +0 -0
  31. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/auth.py +0 -0
  32. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/cli.py +0 -0
  33. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/__init__.py +0 -0
  34. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/lib/__init__.py +0 -0
  35. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/lib/page.py +0 -0
  36. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/lib/widgets.py +0 -0
  37. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/pages/__init__.py +0 -0
  38. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/client/pages/graph_page.py +0 -0
  39. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/__init__.py +0 -0
  40. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/auth.py +0 -0
  41. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/info.py +0 -0
  42. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/publish.py +0 -0
  43. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/studio.py +0 -0
  44. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/commands/update.py +0 -0
  45. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio/assets/index-DDV79SDu.js +0 -0
  46. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio/assets/index-DdMMONxd.css +0 -0
  47. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio/index.html +0 -0
  48. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio/jac_logo.png +0 -0
  49. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio/tauri.svg +0 -0
  50. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio/vite.svg +0 -0
  51. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio-auth/assets/index-Bh6lyeXA.js +0 -0
  52. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio-auth/assets/index-DdMMONxd.css +0 -0
  53. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio-auth/index.html +0 -0
  54. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio-auth/jac_logo.png +0 -0
  55. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio-auth/tauri.svg +0 -0
  56. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/studio-auth/vite.svg +0 -0
  57. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/action_info.yaml +0 -0
  58. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_descriptor.yaml +0 -0
  59. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_info.yaml +0 -0
  60. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_knowledge.yaml +0 -0
  61. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/agent_memory.yaml +0 -0
  62. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/README.md +0 -0
  63. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/actions/README.md +0 -0
  64. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/globals.jac +0 -0
  65. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/main.jac +0 -0
  66. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/exportenv.sh +0 -0
  67. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/importagent.sh +0 -0
  68. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/inituser.sh +0 -0
  69. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/serve.sh +0 -0
  70. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/sh/startclient.sh +0 -0
  71. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/2.0.0/project/tests/README.md +0 -0
  72. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/CHANGELOG.md +0 -0
  73. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/templates/README.md +0 -0
  74. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli/utils.py +0 -0
  75. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli.egg-info/dependency_links.txt +0 -0
  76. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli.egg-info/entry_points.txt +0 -0
  77. {jvcli-2.0.15 → jvcli-2.0.17}/jvcli.egg-info/top_level.txt +0 -0
  78. {jvcli-2.0.15 → jvcli-2.0.17}/setup.cfg +0 -0
  79. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_api.py +0 -0
  80. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_auth.py +0 -0
  81. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_cli.py +0 -0
  82. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_download.py +0 -0
  83. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_info.py +0 -0
  84. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_publish.py +0 -0
  85. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_studio.py +0 -0
  86. {jvcli-2.0.15 → jvcli-2.0.17}/tests/test_update.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jvcli
3
- Version: 2.0.15
3
+ Version: 2.0.17
4
4
  Summary: CLI tool for Jivas Package Repository
5
5
  Home-page: https://github.com/TrueSelph/jvcli
6
6
  Author: TrueSelph Inc.
@@ -12,7 +12,7 @@ Requires-Dist: click>=8.1.8
12
12
  Requires-Dist: requests>=2.32.3
13
13
  Requires-Dist: packaging>=24.2
14
14
  Requires-Dist: pyaml>=25.1.0
15
- Requires-Dist: jac-cloud>=0.1.19
15
+ Requires-Dist: jac-cloud==0.1.20
16
16
  Requires-Dist: streamlit>=1.42.0
17
17
  Requires-Dist: streamlit-elements>=0.1.0
18
18
  Requires-Dist: streamlit-router>=0.1.8
@@ -4,5 +4,5 @@ jvcli package initialization.
4
4
  This package provides the CLI tool for Jivas Package Repository.
5
5
  """
6
6
 
7
- __version__ = "2.0.15"
7
+ __version__ = "2.0.17"
8
8
  __supported__jivas__versions__ = ["2.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 analytics_page, chat_page, dashboard_page, graph_page
12
-
13
- JIVAS_URL = os.environ.get("JIVAS_URL", "http://localhost:8000")
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"{JIVAS_URL}/user/login"
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(dashboard_page.render, "Actions", "/actions").st_button()
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", on_click=dashboard_page.logout, use_container_width=True
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 dashboard page of the jvclient with actions data."""
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("dashboard"):
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 dashboard manually without using `with` if it's not a context manager
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 = action.get("version", "0.0.0")
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
- JIVAS_URL = os.environ.get("JIVAS_URL", "http://localhost:8000")
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
- ctx = get_user_info()
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
- selected_agent = st.session_state.get("selected_agent")
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"{JIVAS_URL}/walker/get_interactions_by_date"
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"{JIVAS_URL}/walker/get_users_by_date"
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"{JIVAS_URL}/walker/get_channels_by_date"
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
- JIVAS_URL = os.environ.get("JIVAS_URL", "http://localhost:8000")
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"{JIVAS_URL}/action/walker"
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"{JIVAS_URL}/interact"
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("JIVAS_URL", "http://localhost:8000"),
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["JIVAS_URL"] = jivas_url
33
+ os.environ["JIVAS_BASE_URL"] = jivas_url
34
34
  os.environ["JIVAS_STUDIO_URL"] = studio_url
35
35
  subprocess.call(
36
36
  [
@@ -331,7 +331,7 @@ def create_namespace(name: str) -> None:
331
331
  )
332
332
  @click.option(
333
333
  "--path",
334
- default="./dafs",
334
+ default="./daf",
335
335
  show_default=True,
336
336
  help="Directory to create the agent.",
337
337
  )
@@ -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 = "dafs"
90
+ base_dir = "daf"
91
91
  elif pkg_type == "action" and package_type.endswith("action"):
92
92
  base_dir = "actions"
93
93
  else:
@@ -16,12 +16,18 @@ from jvcli.utils import TEMPLATES_DIR
16
16
  show_default=True,
17
17
  help="Jivas project version to use for scaffolding.",
18
18
  )
19
- def startproject(project_name: str, version: str) -> None:
19
+ @click.option(
20
+ "--no-env",
21
+ is_flag=True,
22
+ default=False,
23
+ help="Skip generating the .env file.",
24
+ )
25
+ def startproject(project_name: str, version: str, no_env: bool) -> None:
20
26
  """
21
27
  Initialize a new Jivas project with the necessary structure.
22
28
 
23
29
  Usage:
24
- jvcli startproject <project_name> [--version <jivas_version>]
30
+ jvcli startproject <project_name> [--version <jivas_version>] [--no-env]
25
31
  """
26
32
 
27
33
  template_path = os.path.join(TEMPLATES_DIR, version, "project")
@@ -69,12 +75,13 @@ def startproject(project_name: str, version: str) -> None:
69
75
  gitignore_file.write(contents)
70
76
 
71
77
  if file_name == "env.example":
72
- # Write `.env`
73
- target_file_path_env = os.path.join(target_dir, ".env")
74
- with open(target_file_path_env, "w") as env_file:
75
- env_file.write(contents)
78
+ # Write `.env` only if no-env flag is not set
79
+ if not no_env:
80
+ target_file_path_env = os.path.join(target_dir, ".env")
81
+ with open(target_file_path_env, "w") as env_file:
82
+ env_file.write(contents)
76
83
 
77
- # Write `env.example`
84
+ # Always write `env.example`
78
85
  target_file_path_example = os.path.join(target_dir, "env.example")
79
86
  with open(target_file_path_example, "w") as example_file:
80
87
  example_file.write(contents)
@@ -83,7 +90,7 @@ def startproject(project_name: str, version: str) -> None:
83
90
  project_file.write(contents)
84
91
 
85
92
  click.secho(
86
- f"Successfully created Jivas project: {project_name} (Version: {version})",
93
+ f"Successfully created Jivas project: {project_name} (Version: {version}){' (without .env file)' if no_env else ''}",
87
94
  fg="green",
88
95
  )
89
96
 
@@ -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 `./dafs`.
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**: