jvcli 2.0.31__py3-none-any.whl → 2.1.2__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.
Files changed (59) hide show
  1. jvcli/__init__.py +4 -2
  2. jvcli/cli.py +2 -6
  3. jvcli/commands/client.py +14 -38
  4. jvcli/commands/create.py +91 -86
  5. jvcli/commands/server.py +0 -6
  6. jvcli/commands/startproject.py +1 -1
  7. jvcli/templates/{2.0.0 → 2.1.2}/project/README.md +4 -40
  8. jvcli/templates/{2.0.0 → 2.1.2}/project/actions/README.md +1 -1
  9. jvcli/templates/{2.0.0 → 2.1.2}/project/daf/README.md +1 -1
  10. jvcli/templates/{2.0.0 → 2.1.2}/project/env.example +7 -4
  11. jvcli/templates/2.1.2/project/main.jac +2 -0
  12. jvcli/templates/2.1.2/sourcefiles/action_app.py +23 -0
  13. jvcli/templates/2.1.2/sourcefiles/action_archetype.jac +49 -0
  14. jvcli/templates/{2.0.0 → 2.1.2/sourcefiles}/action_info.yaml +1 -1
  15. jvcli/templates/2.1.2/sourcefiles/action_lib.jac +3 -0
  16. jvcli/templates/{2.0.0 → 2.1.2/sourcefiles}/agent_descriptor.yaml +4 -4
  17. jvcli/templates/2.1.2/sourcefiles/interact_action_archetype.jac +58 -0
  18. jvcli/utils.py +1 -1
  19. {jvcli-2.0.31.dist-info → jvcli-2.1.2.dist-info}/METADATA +8 -47
  20. jvcli-2.1.2.dist-info/RECORD +40 -0
  21. jvcli/client/__init__.py +0 -1
  22. jvcli/client/app.py +0 -188
  23. jvcli/client/lib/__init__.py +0 -1
  24. jvcli/client/lib/page.py +0 -68
  25. jvcli/client/lib/utils.py +0 -312
  26. jvcli/client/lib/widgets.py +0 -295
  27. jvcli/client/pages/__init__.py +0 -1
  28. jvcli/client/pages/action_dashboard_page.py +0 -120
  29. jvcli/client/pages/analytics_page.py +0 -245
  30. jvcli/client/pages/chat_page.py +0 -150
  31. jvcli/client/pages/graph_page.py +0 -20
  32. jvcli/commands/clean.py +0 -29
  33. jvcli/commands/studio.py +0 -258
  34. jvcli/studio/assets/index-DDV79SDu.js +0 -213
  35. jvcli/studio/assets/index-DdMMONxd.css +0 -1
  36. jvcli/studio/index.html +0 -15
  37. jvcli/studio/jac_logo.png +0 -0
  38. jvcli/studio/tauri.svg +0 -6
  39. jvcli/studio/vite.svg +0 -1
  40. jvcli/studio-auth/assets/index-Bh6lyeXA.js +0 -218
  41. jvcli/studio-auth/assets/index-DdMMONxd.css +0 -1
  42. jvcli/studio-auth/index.html +0 -15
  43. jvcli/studio-auth/jac_logo.png +0 -0
  44. jvcli/studio-auth/tauri.svg +0 -6
  45. jvcli/studio-auth/vite.svg +0 -1
  46. jvcli/templates/2.0.0/project/main.jac +0 -2
  47. jvcli-2.0.31.dist-info/RECORD +0 -61
  48. /jvcli/templates/{2.0.0 → 2.1.2}/project/gitignore.example +0 -0
  49. /jvcli/templates/{2.0.0 → 2.1.2}/project/globals.jac +0 -0
  50. /jvcli/templates/{2.0.0 → 2.1.2}/project/tests/README.md +0 -0
  51. /jvcli/templates/{CHANGELOG.md → 2.1.2/sourcefiles/CHANGELOG.md} +0 -0
  52. /jvcli/templates/{README.md → 2.1.2/sourcefiles/README.md} +0 -0
  53. /jvcli/templates/{2.0.0 → 2.1.2/sourcefiles}/agent_info.yaml +0 -0
  54. /jvcli/templates/{2.0.0 → 2.1.2/sourcefiles}/agent_knowledge.yaml +0 -0
  55. /jvcli/templates/{2.0.0 → 2.1.2/sourcefiles}/agent_memory.yaml +0 -0
  56. {jvcli-2.0.31.dist-info → jvcli-2.1.2.dist-info}/WHEEL +0 -0
  57. {jvcli-2.0.31.dist-info → jvcli-2.1.2.dist-info}/entry_points.txt +0 -0
  58. {jvcli-2.0.31.dist-info → jvcli-2.1.2.dist-info}/licenses/LICENSE +0 -0
  59. {jvcli-2.0.31.dist-info → jvcli-2.1.2.dist-info}/top_level.txt +0 -0
@@ -1,120 +0,0 @@
1
- """Render the action_dashboard page of the jvclient with actions data."""
2
-
3
- import streamlit as st
4
- from streamlit_elements import dashboard, elements, mui
5
- from streamlit_router import StreamlitRouter
6
-
7
- from jvcli.client.lib.page import Page
8
-
9
-
10
- def render(router: StreamlitRouter) -> None:
11
- """Render the dashboard page."""
12
- if actions_data := st.session_state.get("actions_data"):
13
- with elements("action_dashboard"):
14
- columns = 4
15
- layout = []
16
-
17
- # Compute the position of each card component in the layout
18
- for idx, _ in enumerate(actions_data):
19
- x = (idx % columns) * 3
20
- y = (idx // columns) * 2
21
- width = 3
22
- height = 2
23
- # Add an item to the action_dashboard manually without using `with` if it's not a context manager
24
- layout.append(
25
- dashboard.Item(
26
- f"card_{idx}",
27
- x,
28
- y,
29
- width,
30
- height,
31
- isDraggable=False,
32
- isResizable=False,
33
- )
34
- )
35
-
36
- # now populate the actual cards with content
37
- with dashboard.Grid(layout):
38
- for idx, action in enumerate(actions_data):
39
-
40
- package = action.get("_package", {})
41
- title = package.get("meta", {}).get("title", action.get("label"))
42
- description = action.get("description", "")
43
- version = package.get("version", "0.0.0")
44
- action_type = package.get("meta", {}).get("type", "action")
45
- key = Page.normalize_label(title)
46
- enabled_color = "red"
47
- enabled_text = "(disabled)"
48
- avatar_text = "A"
49
-
50
- if action_type == "interact_action":
51
- avatar_text = "I"
52
-
53
- if action.get("enabled", False):
54
- enabled_color = "green"
55
- enabled_text = ""
56
-
57
- # create the card
58
- with mui.Card(
59
- key=f"card_{idx}",
60
- sx={
61
- "display": "flex",
62
- "flexDirection": "column",
63
- "borderRadius": 2,
64
- "overflow": "scroll",
65
- },
66
- elevation=2,
67
- ):
68
- # Card header with title
69
- mui.CardHeader(
70
- title=f"{title} {enabled_text}",
71
- subheader=f"{version}",
72
- avatar=mui.Avatar(
73
- avatar_text, sx={"bgcolor": enabled_color}
74
- ),
75
- action=mui.IconButton(mui.icon.MoreVert),
76
- )
77
-
78
- # Card body
79
- with mui.CardContent(sx={"flex": 1}):
80
- mui.Typography(description, variant="body2")
81
-
82
- # Card footer with action buttons
83
- with mui.CardActions(disableSpacing=True):
84
- query = st.query_params.to_dict()
85
- query_str = ""
86
- for k, v in query.items():
87
- if k != "request":
88
- query_str += f"{k}={v}&"
89
-
90
- if package.get("config", {}).get("app", False):
91
- with mui.Stack(
92
- direction="row",
93
- spacing=2,
94
- alignItems="center",
95
- sx={"padding": "10px"},
96
- ):
97
- mui.Button(
98
- "Configure",
99
- variant="outlined",
100
- href=(
101
- (
102
- f"/?request=GET:/{key}&${query_str.rstrip('&')}"
103
- if query_str
104
- else f"/?request=GET:/{key}"
105
- )
106
- + f"&token={st.session_state.TOKEN}"
107
- ),
108
- target="_blank",
109
- )
110
-
111
-
112
- def logout() -> None:
113
- """Logout the user by clearing the session token."""
114
- del st.session_state["TOKEN"]
115
- token_query = st.query_params.get("token")
116
-
117
- if token_query:
118
- query_params = st.query_params.to_dict()
119
- del query_params["token"]
120
- st.query_params.from_dict(query_params)
@@ -1,245 +0,0 @@
1
- """Renders the analytics page of the JVCLI client."""
2
-
3
- import calendar
4
- import datetime
5
- import os
6
- from typing import Optional
7
-
8
- import pandas as pd
9
- import requests
10
- import streamlit as st
11
- from streamlit.delta_generator import DeltaGenerator
12
- from streamlit_javascript import st_javascript
13
- from streamlit_router import StreamlitRouter
14
-
15
- from jvcli.client.lib.utils import call_healthcheck, get_user_info
16
-
17
- JIVAS_BASE_URL = os.environ.get("JIVAS_BASE_URL", "http://localhost:8000")
18
-
19
-
20
- def render(router: StreamlitRouter) -> None:
21
- """Render the analytics page."""
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
- st.rerun()
83
-
84
- else:
85
- st.error("Failed to fetch healthcheck data.")
86
- except Exception as e:
87
- st.error("An error occurred while fetching healthcheck data.")
88
- print(e)
89
-
90
- st.header("Analytics", divider=True)
91
- today = datetime.date.today()
92
- last_day = calendar.monthrange(today.year, today.month)[1]
93
-
94
- date_range = st.date_input(
95
- "Period",
96
- (
97
- datetime.date(today.year, today.month, 1),
98
- datetime.date(today.year, today.month, last_day),
99
- ),
100
- )
101
-
102
- (start_date, end_date) = date_range
103
-
104
- # rerender_metrics = render_metrics()
105
- col1, col2, col3 = st.columns(3)
106
- timezone = st_javascript(
107
- """await (async () => {
108
- const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
109
- console.log(userTimezone)
110
- return userTimezone
111
- })().then(returnValue => returnValue)"""
112
- )
113
-
114
- try:
115
- ctx = get_user_info()
116
- if selected_agent and end_date > start_date:
117
- interactions_chart(
118
- token=ctx["token"],
119
- agent_id=selected_agent["id"],
120
- start_date=start_date,
121
- end_date=end_date,
122
- metric_col=col1,
123
- timezone=timezone,
124
- )
125
- users_chart(
126
- token=ctx["token"],
127
- agent_id=selected_agent["id"],
128
- start_date=start_date,
129
- end_date=end_date,
130
- metric_col=col2,
131
- timezone=timezone,
132
- )
133
- channels_chart(
134
- token=ctx["token"],
135
- agent_id=selected_agent["id"],
136
- start_date=start_date,
137
- end_date=end_date,
138
- metric_col=col3,
139
- timezone=timezone,
140
- )
141
- else:
142
- st.text("Invalid date range")
143
- except Exception as e:
144
- st.text("Unable to render charts")
145
- print(e)
146
-
147
-
148
- def interactions_chart(
149
- start_date: datetime.date,
150
- end_date: datetime.date,
151
- agent_id: str,
152
- token: str,
153
- metric_col: DeltaGenerator,
154
- timezone: str,
155
- ) -> None:
156
- """Render the interactions chart."""
157
- url = f"{JIVAS_BASE_URL}/walker/get_interactions_by_date"
158
-
159
- with st.container(border=True):
160
- st.subheader("Interactions by Date")
161
- response = requests.post(
162
- url=url,
163
- json={
164
- "agent_id": agent_id,
165
- "reporting": True,
166
- "start_date": start_date.isoformat(),
167
- "end_date": end_date.isoformat(),
168
- "timezone": timezone,
169
- },
170
- headers={"Authorization": f"Bearer {token}"},
171
- )
172
- if response.status_code == 200:
173
- if response_data := response.json():
174
- chart_data = pd.DataFrame(
175
- data=response_data["reports"][0]["data"],
176
- )
177
- st.line_chart(chart_data, x="date", y="count")
178
- total = response_data["reports"][0]["total"]
179
- metric_col.metric("Interactions", total)
180
-
181
-
182
- def users_chart(
183
- start_date: datetime.date,
184
- end_date: datetime.date,
185
- agent_id: str,
186
- token: str,
187
- metric_col: DeltaGenerator,
188
- timezone: str,
189
- ) -> None:
190
- """Render the users chart."""
191
- url = f"{JIVAS_BASE_URL}/walker/get_users_by_date"
192
- with st.container(border=True):
193
- st.subheader("Users by Date")
194
- response = requests.post(
195
- url=url,
196
- json={
197
- "agent_id": agent_id,
198
- "reporting": True,
199
- "start_date": start_date.isoformat(),
200
- "end_date": end_date.isoformat(),
201
- "timezone": timezone,
202
- },
203
- headers={"Authorization": f"Bearer {token}"},
204
- )
205
- if response.status_code == 200:
206
- if response_data := response.json():
207
- chart_data = pd.DataFrame(
208
- data=response_data["reports"][0]["data"],
209
- )
210
- st.line_chart(chart_data, x="date", y="count")
211
- total = response_data["reports"][0]["total"]
212
- metric_col.metric("Users", total)
213
-
214
-
215
- def channels_chart(
216
- start_date: datetime.date,
217
- end_date: datetime.date,
218
- agent_id: str,
219
- token: str,
220
- metric_col: DeltaGenerator,
221
- timezone: str,
222
- ) -> None:
223
- """Render the channels chart."""
224
- url = f"{JIVAS_BASE_URL}/walker/get_channels_by_date"
225
- with st.container(border=True):
226
- st.subheader("Channels by Date")
227
- response = requests.post(
228
- url=url,
229
- json={
230
- "agent_id": agent_id,
231
- "reporting": True,
232
- "start_date": start_date.isoformat(),
233
- "end_date": end_date.isoformat(),
234
- "timezone": timezone,
235
- },
236
- headers={"Authorization": f"Bearer {token}"},
237
- )
238
- if response.status_code == 200:
239
- if response_data := response.json():
240
- chart_data = pd.DataFrame(
241
- data=response_data["reports"][0]["data"],
242
- )
243
- st.line_chart(chart_data, x="date", y="count")
244
- total = response_data["reports"][0]["total"]
245
- metric_col.metric("Channels", total)
@@ -1,150 +0,0 @@
1
- """Renders the chat page for the JVCLI client."""
2
-
3
- import os
4
- from typing import Optional
5
-
6
- import requests
7
- import streamlit as st
8
- from streamlit_router import StreamlitRouter
9
-
10
- from jvcli.client.lib.utils import get_user_info
11
-
12
- JIVAS_BASE_URL = os.environ.get("JIVAS_BASE_URL", "http://localhost:8000")
13
-
14
-
15
- def transcribe_audio(token: str, agent_id: str, file: bytes) -> dict:
16
- """Transcribe audio using the walker API."""
17
- action_walker_url = f"{JIVAS_BASE_URL}/action/walker"
18
-
19
- # Create form data
20
- files = {"attachments": ("audio.wav", file, "audio/wav")}
21
-
22
- data = {
23
- "args": "{}",
24
- "module_root": "jivas.agent.action",
25
- "agent_id": agent_id,
26
- "walker": "invoke_stt_action",
27
- }
28
-
29
- headers = {"Authorization": f"Bearer {token}"}
30
-
31
- # Make the POST request
32
- response = requests.post(
33
- f"{action_walker_url}", headers=headers, data=data, files=files
34
- )
35
-
36
- # Parse JSON response
37
- result = response.json()
38
- return result
39
-
40
-
41
- def render(router: StreamlitRouter) -> None:
42
- """Render the chat page."""
43
- url = f"{JIVAS_BASE_URL}/interact"
44
- ctx = get_user_info()
45
-
46
- st.header("Chat", divider=True)
47
- tts_on = st.toggle("TTS")
48
-
49
- message = ""
50
- # Handle audio input first
51
- audio_value = st.audio_input("Record a voice message")
52
- # Get selected agent from query params
53
- selected_agent = st.query_params.get("agent")
54
-
55
- if selected_agent:
56
- chat_messages = st.session_state.messages.get(selected_agent, [])
57
-
58
- # Display chat messages from history on app rerun
59
- for item in chat_messages:
60
- role = item.get("role", "user")
61
- content = item.get("content", "")
62
- with st.chat_message(role):
63
- st.markdown(content)
64
- if payload := item.get("payload"):
65
- with st.expander("...", False):
66
- st.json(payload)
67
-
68
- # Process audio input if available
69
- if audio_value and selected_agent:
70
- result = transcribe_audio(ctx["token"], selected_agent, audio_value)
71
- if result and result.get("success", False):
72
- message = result["transcript"]
73
-
74
- # Otherwise handle text input
75
- if input := st.chat_input("Type your message here"):
76
- message = input
77
-
78
- if message:
79
- send_message(message, url, ctx["token"], selected_agent, tts_on)
80
-
81
-
82
- def send_message(
83
- prompt: str,
84
- url: str,
85
- token: str,
86
- selected_agent: Optional[str] = None,
87
- tts_on: bool = False,
88
- ) -> None:
89
- """Send a message to the walker API and display the response."""
90
- # Add user message to chat history
91
- add_agent_message(selected_agent, {"role": "user", "content": prompt})
92
-
93
- # Display user message in chat message container
94
- with st.chat_message("user"):
95
- st.markdown(prompt)
96
-
97
- with st.chat_message("assistant"):
98
- # Call walker API
99
- response = requests.post(
100
- url=url,
101
- json={
102
- "utterance": prompt,
103
- "session_id": st.session_state.session_id,
104
- "agent_id": selected_agent,
105
- "tts": tts_on,
106
- "verbose": True,
107
- },
108
- headers={"Authorization": f"Bearer {token}"},
109
- )
110
- if response.status_code == 200:
111
- if response_data := response.json():
112
- st.markdown(
113
- response_data.get("response", {})
114
- .get("message", {})
115
- .get("content", "...")
116
- )
117
- if "audio_url" in response_data.get("response", {}) and tts_on:
118
- audio_url = response_data.get("response", {}).get(
119
- "audio_url", "..."
120
- )
121
- st.audio(audio_url, autoplay=True)
122
- with st.expander("...", False):
123
- st.json(response_data)
124
-
125
- # Add assistant response to chat history
126
- add_agent_message(
127
- selected_agent,
128
- {
129
- "role": "assistant",
130
- "content": response_data.get("response", {})
131
- .get("message", {})
132
- .get("content", "..."),
133
- "payload": response_data,
134
- },
135
- )
136
- if "session_id" in response_data.get("response", {}):
137
- st.session_state.session_id = response_data["response"]["session_id"]
138
-
139
-
140
- def add_agent_message(agent_id: Optional[str], message: dict) -> None:
141
- """Add a message to the chat history for a specific agent."""
142
- all_messages = st.session_state.messages
143
- agent_messages = all_messages.get(agent_id, [])
144
- agent_messages.append(message)
145
- st.session_state.messages[agent_id] = agent_messages
146
-
147
-
148
- def clear_messages() -> None:
149
- """Clear all chat messages."""
150
- st.session_state.messages = {}
@@ -1,20 +0,0 @@
1
- """Render the Jivas Studio page in an iframe."""
2
-
3
- import os
4
-
5
- import streamlit as st
6
- from streamlit_router import StreamlitRouter
7
-
8
- JIVAS_STUDIO_URL = os.environ.get("JIVAS_STUDIO_URL", "http://localhost:8989")
9
-
10
-
11
- def render(router: StreamlitRouter) -> None:
12
- """
13
- Render the Jivas Studio page in an iframe.
14
-
15
- args:
16
- router: StreamlitRouter
17
- The StreamlitRouter instance
18
- """
19
-
20
- st.components.v1.iframe(JIVAS_STUDIO_URL, width=None, height=800, scrolling=False)
jvcli/commands/clean.py DELETED
@@ -1,29 +0,0 @@
1
- """Runs the jac clean command"""
2
-
3
- import subprocess
4
-
5
- import click
6
-
7
-
8
- @click.command(
9
- help="Clean the Jac files in the current directory and subdirectories. This command executes 'jac clean', which removes compiled Jac artifacts and temporary files. "
10
- "Use this command to ensure a clean state before rebuilding your Jac project."
11
- )
12
- @click.pass_context
13
- def clean(ctx: click.Context) -> None:
14
- """Clean the Jac files in directory."""
15
- try:
16
- click.echo("Running jac clean in actions directory...")
17
- result = subprocess.run(["jac", "clean"], check=True)
18
- if result.returncode == 0:
19
- click.echo("Successfully cleaned directory.")
20
-
21
- else:
22
- click.secho("Failed to clean directory.", fg="red")
23
- ctx.exit(1)
24
- except subprocess.CalledProcessError as e:
25
- click.secho(f"Error running jac clean: {e}", fg="red")
26
- ctx.exit(1)
27
- except Exception as e:
28
- click.secho(f"Unexpected error: {e}", fg="red")
29
- ctx.exit(1)