meshagent-cli 0.0.36__py3-none-any.whl → 0.0.38__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.

Potentially problematic release.


This version of meshagent-cli might be problematic. Click here for more details.

meshagent/cli/call.py CHANGED
@@ -1,35 +1,37 @@
1
-
2
1
  import typer
3
2
  from rich import print
4
3
  from typing import Annotated, Optional
5
4
  import json
6
5
  import aiohttp
7
- from meshagent.api import RoomClient, ParticipantToken, WebSocketClientProtocol, RoomException, ParticipantGrant
6
+ from meshagent.api import (
7
+ RoomClient,
8
+ ParticipantToken,
9
+ WebSocketClientProtocol,
10
+ ParticipantGrant,
11
+ )
8
12
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
9
13
  from meshagent.api.services import send_webhook
10
14
  from meshagent.cli import async_typer
11
- from meshagent.cli.helper import get_client, print_json_table, set_active_project, resolve_project_id
12
- from meshagent.cli.helper import set_active_project, get_active_project, resolve_project_id, resolve_api_key
13
-
14
- app = async_typer.AsyncTyper()
15
-
15
+ from meshagent.cli.helper import get_client, resolve_project_id
16
+ from meshagent.cli.helper import resolve_api_key
16
17
  from urllib.parse import urlparse
17
18
  from pathlib import PurePath
18
19
  import socket
19
20
  import ipaddress
20
21
 
22
+ app = async_typer.AsyncTyper()
23
+
21
24
  PRIVATE_NETS = (
22
25
  ipaddress.ip_network("10.0.0.0/8"),
23
26
  ipaddress.ip_network("172.16.0.0/12"),
24
27
  ipaddress.ip_network("192.168.0.0/16"),
25
- ipaddress.ip_network("169.254.0.0/16"), # IPv4 link-local
26
- ipaddress.ip_network("fc00::/7"), # IPv6 unique-local
27
- ipaddress.ip_network("fe80::/10"), # IPv6 link-local
28
+ ipaddress.ip_network("169.254.0.0/16"), # IPv4 link-local
29
+ ipaddress.ip_network("fc00::/7"), # IPv6 unique-local
30
+ ipaddress.ip_network("fe80::/10"), # IPv6 link-local
28
31
  )
29
32
 
30
33
 
31
34
  def is_local_url(url: str) -> bool:
32
-
33
35
  """
34
36
  Return True if *url* points to the local machine or a private-LAN host.
35
37
  """
@@ -42,14 +44,14 @@ def is_local_url(url: str) -> bool:
42
44
 
43
45
  # 2. Quick loop-back check on hostname literal
44
46
  hostname = parsed.hostname
45
- if hostname in {"localhost", None}: # None ⇒ something like "http:///path"
47
+ if hostname in {"localhost", None}: # None ⇒ something like "http:///path"
46
48
  return True
47
49
 
48
50
  try:
49
51
  # Accept both direct IP literals and DNS names
50
52
  addr_info = socket.getaddrinfo(hostname, None)
51
53
  except socket.gaierror:
52
- return False # Unresolvable host ⇒ treat as non-local (or raise)
54
+ return False # Unresolvable host ⇒ treat as non-local (or raise)
53
55
 
54
56
  for *_, sockaddr in addr_info:
55
57
  ip_str = sockaddr[0]
@@ -75,7 +77,9 @@ async def make_call(
75
77
  local: Optional[bool] = None,
76
78
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
77
79
  url: Annotated[str, typer.Option(..., help="URL the agent should call")],
78
- arguments: Annotated[str, typer.Option(..., help="JSON string with arguments for the call")] = {}
80
+ arguments: Annotated[
81
+ str, typer.Option(..., help="JSON string with arguments for the call")
82
+ ] = {},
79
83
  ):
80
84
  """
81
85
  Instruct an agent to 'call' a given URL with specific arguments.
@@ -84,46 +88,51 @@ async def make_call(
84
88
  try:
85
89
  project_id = await resolve_project_id(project_id=project_id)
86
90
  api_key_id = await resolve_api_key(project_id, api_key_id)
87
-
88
- key = (await account_client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
91
+
92
+ key = (
93
+ await account_client.decrypt_project_api_key(
94
+ project_id=project_id, id=api_key_id
95
+ )
96
+ )["token"]
89
97
 
90
98
  token = ParticipantToken(
91
- name=name,
92
- project_id=project_id,
93
- api_key_id=api_key_id
99
+ name=name, project_id=project_id, api_key_id=api_key_id
94
100
  )
95
101
  token.add_role_grant(role=role)
96
102
  token.add_room_grant(room)
97
103
  token.grants.append(ParticipantGrant(name="tunnel_ports", scope="9000"))
98
104
 
99
- if local == None:
105
+ if local is None:
100
106
  local = is_local_url(url)
101
-
107
+
102
108
  if local:
103
109
  async with aiohttp.ClientSession() as session:
104
-
105
- event="room.call"
106
- data={
107
- "room_url" : websocket_room_url(room_name=room),
108
- "room_name" : room,
109
- "token" : token.to_jwt(token=key),
110
- "arguments" : arguments,
110
+ event = "room.call"
111
+ data = {
112
+ "room_url": websocket_room_url(room_name=room),
113
+ "room_name": room,
114
+ "token": token.to_jwt(token=key),
115
+ "arguments": arguments,
111
116
  }
112
-
113
- await send_webhook(session=session, url=url, event=event, data=data, secret=None)
117
+
118
+ await send_webhook(
119
+ session=session, url=url, event=event, data=data, secret=None
120
+ )
114
121
  else:
115
122
  print("[bold green]Connecting to room...[/bold green]")
116
123
  async with RoomClient(
117
- protocol=WebSocketClientProtocol(url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
118
- token=token.to_jwt(token=key))
124
+ protocol=WebSocketClientProtocol(
125
+ url=websocket_room_url(
126
+ room_name=room, base_url=meshagent_base_url()
127
+ ),
128
+ token=token.to_jwt(token=key),
129
+ )
119
130
  ) as client:
120
131
  print("[bold green]Making agent call...[/bold green]")
121
132
  await client.agents.make_call(
122
- name=agent_name,
123
- url=url,
124
- arguments=json.loads(arguments)
133
+ name=agent_name, url=url, arguments=json.loads(arguments)
125
134
  )
126
135
  print("[bold cyan]Call request sent successfully.[/bold cyan]")
127
-
136
+
128
137
  finally:
129
138
  await account_client.close()
meshagent/cli/chatbot.py CHANGED
@@ -1,12 +1,17 @@
1
-
2
1
  import typer
3
2
  from rich import print
4
3
  from typing import Annotated, Optional
5
4
  from meshagent.tools import Toolkit
6
- from meshagent.api import RoomClient, ParticipantToken, WebSocketClientProtocol, RoomException
5
+ from meshagent.api import RoomClient, WebSocketClientProtocol
7
6
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
8
7
  from meshagent.cli import async_typer
9
- from meshagent.cli.helper import get_client, resolve_project_id, resolve_api_key, resolve_token_jwt, resolve_room
8
+ from meshagent.cli.helper import (
9
+ get_client,
10
+ resolve_project_id,
11
+ resolve_api_key,
12
+ resolve_token_jwt,
13
+ resolve_room,
14
+ )
10
15
  from meshagent.agents.chat import ChatBot
11
16
  from meshagent.openai import OpenAIResponsesAdapter
12
17
  from meshagent.openai.tools.responses_adapter import LocalShellTool
@@ -21,24 +26,24 @@ from meshagent.api import RequiredToolkit, RequiredSchema
21
26
  app = async_typer.AsyncTyper()
22
27
 
23
28
 
24
- def build_chatbot(*,
29
+ def build_chatbot(
30
+ *,
25
31
  model: str,
26
- agent_name: str,
32
+ agent_name: str,
27
33
  rule: List[str],
28
34
  toolkit: List[str],
29
35
  schema: List[str],
30
36
  image_generation: Optional[str] = None,
31
37
  local_shell: bool,
32
- computer_use: bool
38
+ computer_use: bool,
33
39
  ):
34
-
35
40
  requirements = []
36
-
41
+
37
42
  toolkits = []
38
43
 
39
44
  for t in toolkit:
40
45
  requirements.append(RequiredToolkit(name=t))
41
-
46
+
42
47
  for t in schema:
43
48
  requirements.append(RequiredSchema(name=t))
44
49
 
@@ -49,16 +54,13 @@ def build_chatbot(*,
49
54
  llm_adapter = OpenAIResponsesAdapter(
50
55
  model=model,
51
56
  response_options={
52
- "reasoning" : {
53
- "generate_summary" : "concise"
54
- },
55
- "truncation" : "auto"
56
- }
57
+ "reasoning": {"generate_summary": "concise"},
58
+ "truncation": "auto",
59
+ },
57
60
  )
58
61
  else:
59
- llm_adapter = OpenAIResponsesAdapter(
60
- model=model
61
- )
62
+ llm_adapter = OpenAIResponsesAdapter(model=model)
63
+
62
64
  class CustomChatbot(BaseClass):
63
65
  def __init__(self):
64
66
  super().__init__(
@@ -66,31 +68,35 @@ def build_chatbot(*,
66
68
  name=agent_name,
67
69
  requires=requirements,
68
70
  toolkits=toolkits,
69
- rules=rule if len(rule) > 0 else None
71
+ rules=rule if len(rule) > 0 else None,
70
72
  )
71
-
73
+
72
74
  async def get_thread_toolkits(self, *, thread_context, participant):
73
- toolkits = await super().get_thread_toolkits(thread_context=thread_context, participant=participant)
74
-
75
+ toolkits = await super().get_thread_toolkits(
76
+ thread_context=thread_context, participant=participant
77
+ )
78
+
75
79
  thread_toolkit = Toolkit(name="thread_toolkit", tools=[])
76
80
 
77
81
  if local_shell:
78
- thread_toolkit.tools.append(LocalShellTool())
82
+ thread_toolkit.tools.append(LocalShellTool())
79
83
 
80
- if image_generation != None:
81
-
84
+ if image_generation is not None:
82
85
  print("adding openai image gen to thread", flush=True)
83
- thread_toolkit.tools.append(ChatBotThreadOpenAIImageGenerationTool(
84
- model=image_generation,
85
- thread_context=thread_context,
86
- partial_images=3
87
- ))
88
-
86
+ thread_toolkit.tools.append(
87
+ ChatBotThreadOpenAIImageGenerationTool(
88
+ model=image_generation,
89
+ thread_context=thread_context,
90
+ partial_images=3,
91
+ )
92
+ )
93
+
89
94
  toolkits.append(thread_toolkit)
90
95
  return toolkits
91
-
96
+
92
97
  return CustomChatbot
93
98
 
99
+
94
100
  @app.async_command("join")
95
101
  async def make_call(
96
102
  *,
@@ -100,15 +106,31 @@ async def make_call(
100
106
  name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
101
107
  role: str = "agent",
102
108
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
103
- token_path: Annotated[Optional[str], typer.Option()] = None,
104
-
109
+ token_path: Annotated[Optional[str], typer.Option()] = None,
105
110
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
106
- toolkit: Annotated[List[str], typer.Option("--toolkit", "-t", help="the name or url of a required toolkit")] = [],
107
- schema: Annotated[List[str], typer.Option("--schema", "-s", help="the name or url of a required schema")] = [],
108
- model: Annotated[str, typer.Option(..., help="Name of the LLM model to use for the chatbot")] = "gpt-4o",
109
- image_generation: Annotated[Optional[str], typer.Option(..., help="Name of an image gen model")] = None,
110
- computer_use: Annotated[Optional[bool], typer.Option(..., help="Enable computer use (requires computer-use-preview model)")] = False,
111
- local_shell: Annotated[Optional[bool], typer.Option(..., help="Enable local shell tool calling")] = False,
111
+ toolkit: Annotated[
112
+ List[str],
113
+ typer.Option("--toolkit", "-t", help="the name or url of a required toolkit"),
114
+ ] = [],
115
+ schema: Annotated[
116
+ List[str],
117
+ typer.Option("--schema", "-s", help="the name or url of a required schema"),
118
+ ] = [],
119
+ model: Annotated[
120
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
121
+ ] = "gpt-4o",
122
+ image_generation: Annotated[
123
+ Optional[str], typer.Option(..., help="Name of an image gen model")
124
+ ] = None,
125
+ computer_use: Annotated[
126
+ Optional[bool],
127
+ typer.Option(
128
+ ..., help="Enable computer use (requires computer-use-preview model)"
129
+ ),
130
+ ] = False,
131
+ local_shell: Annotated[
132
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
133
+ ] = False,
112
134
  ):
113
135
  account_client = await get_client()
114
136
  try:
@@ -116,75 +138,107 @@ async def make_call(
116
138
  api_key_id = await resolve_api_key(project_id, api_key_id)
117
139
 
118
140
  room = resolve_room(room)
119
- jwt = await resolve_token_jwt(project_id=project_id, api_key_id=api_key_id, token_path=token_path, name=name, role=role, room=room)
120
-
141
+ jwt = await resolve_token_jwt(
142
+ project_id=project_id,
143
+ api_key_id=api_key_id,
144
+ token_path=token_path,
145
+ name=name,
146
+ role=role,
147
+ room=room,
148
+ )
149
+
121
150
  print("[bold green]Connecting to room...[/bold green]", flush=True)
122
151
  async with RoomClient(
123
152
  protocol=WebSocketClientProtocol(
124
153
  url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
125
- token=jwt
154
+ token=jwt,
126
155
  )
127
156
  ) as client:
128
-
129
157
  requirements = []
130
-
131
- toolkits = []
132
158
 
133
159
  for t in toolkit:
134
160
  requirements.append(RequiredToolkit(name=t))
135
-
161
+
136
162
  for t in schema:
137
163
  requirements.append(RequiredSchema(name=t))
138
164
 
139
- CustomChatbot = build_chatbot(computer_use=computer_use, model=model, local_shell=local_shell, agent_name=agent_name, rule=rule, toolkit=toolkit, schema=schema, image_generation=image_generation)
140
-
141
- bot = CustomChatbot(
142
- llm_adapter=OpenAIResponsesAdapter(),
143
- name=agent_name,
144
- requires=requirements,
145
- toolkits=toolkits,
146
- rules=rule if len(rule) > 0 else None
165
+ CustomChatbot = build_chatbot(
166
+ computer_use=computer_use,
167
+ model=model,
168
+ local_shell=local_shell,
169
+ agent_name=agent_name,
170
+ rule=rule,
171
+ toolkit=toolkit,
172
+ schema=schema,
173
+ image_generation=image_generation,
147
174
  )
148
175
 
176
+ bot = CustomChatbot()
177
+
149
178
  await bot.start(room=client)
150
179
  try:
180
+ print(
181
+ f"[bold green]Open the studio to interact with your agent: {meshagent_base_url().replace('api.', 'studio.')}/projects/{project_id}/rooms/{client.room_name}[/bold green]",
182
+ flush=True,
183
+ )
151
184
  await client.protocol.wait_for_close()
152
185
  except KeyboardInterrupt:
153
186
  await bot.stop()
154
-
187
+
155
188
  finally:
156
189
  await account_client.close()
157
190
 
158
191
 
159
-
160
192
  @app.async_command("service")
161
193
  async def service(
162
194
  *,
163
195
  room: Annotated[Optional[str], typer.Option()] = None,
164
-
165
196
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
166
-
167
197
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
168
- toolkit: Annotated[List[str], typer.Option("--toolkit", "-t", help="the name or url of a required toolkit")] = [],
169
- schema: Annotated[List[str], typer.Option("--schema", "-s", help="the name or url of a required schema")] = [],
170
- model: Annotated[str, typer.Option(..., help="Name of the LLM model to use for the chatbot")] = "gpt-4o",
171
- image_generation: Annotated[Optional[str], typer.Option(..., help="Name of an image gen model")] = None,
172
- local_shell: Annotated[Optional[bool], typer.Option(..., help="Enable local shell tool calling")] = False,
173
- computer_use: Annotated[Optional[bool], typer.Option(..., help="Enable computer use (requires computer-use-preview model)")] = False,
174
-
175
- host: Annotated[Optional[str], typer.Option()] = None,
176
- port: Annotated[Optional[int], typer.Option()] = None,
198
+ toolkit: Annotated[
199
+ List[str],
200
+ typer.Option("--toolkit", "-t", help="the name or url of a required toolkit"),
201
+ ] = [],
202
+ schema: Annotated[
203
+ List[str],
204
+ typer.Option("--schema", "-s", help="the name or url of a required schema"),
205
+ ] = [],
206
+ model: Annotated[
207
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
208
+ ] = "gpt-4o",
209
+ image_generation: Annotated[
210
+ Optional[str], typer.Option(..., help="Name of an image gen model")
211
+ ] = None,
212
+ local_shell: Annotated[
213
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
214
+ ] = False,
215
+ computer_use: Annotated[
216
+ Optional[bool],
217
+ typer.Option(
218
+ ..., help="Enable computer use (requires computer-use-preview model)"
219
+ ),
220
+ ] = False,
221
+ host: Annotated[Optional[str], typer.Option()] = None,
222
+ port: Annotated[Optional[int], typer.Option()] = None,
177
223
  path: Annotated[str, typer.Option()] = "/agent",
178
224
  ):
179
-
180
225
  room = resolve_room(room)
181
-
226
+
182
227
  print("[bold green]Connecting to room...[/bold green]", flush=True)
183
228
 
184
- service = ServiceHost(
185
- host=host,
186
- port=port
229
+ service = ServiceHost(host=host, port=port)
230
+ service.add_path(
231
+ path=path,
232
+ cls=build_chatbot(
233
+ computer_use=computer_use,
234
+ model=model,
235
+ local_shell=local_shell,
236
+ agent_name=agent_name,
237
+ rule=rule,
238
+ toolkit=toolkit,
239
+ schema=schema,
240
+ image_generation=image_generation,
241
+ ),
187
242
  )
188
- service.add_path(path=path, cls=build_chatbot(computer_use=computer_use, model=model, local_shell=local_shell, agent_name=agent_name, rule=rule, toolkit=toolkit, schema=schema, image_generation=image_generation))
189
243
 
190
244
  await service.run()
meshagent/cli/cli.py CHANGED
@@ -22,8 +22,16 @@ from meshagent.cli import tty
22
22
 
23
23
  from meshagent.cli import otel
24
24
 
25
+ from art import tprint
26
+
25
27
  import logging
26
28
 
29
+ import os
30
+ import sys
31
+ from pathlib import Path
32
+ from meshagent.cli.helper import get_client, resolve_project_id, resolve_api_key
33
+
34
+
27
35
  otel.init(level=logging.INFO)
28
36
 
29
37
  # Turn down OpenAI logs, they are a bit noisy
@@ -49,16 +57,11 @@ app.add_typer(chatbot.app, name="chatbot")
49
57
  app.add_typer(voicebot.app, name="voicebot")
50
58
  app.add_typer(tty.app, name="tty")
51
59
 
60
+
52
61
  def _run_async(coro):
53
62
  asyncio.run(coro)
54
63
 
55
64
 
56
- import os, sys
57
- from pathlib import Path
58
- import typer
59
- from meshagent.cli.helper import get_client, set_active_project, get_active_project, resolve_project_id, resolve_api_key
60
-
61
-
62
65
  def detect_shell() -> str:
63
66
  """
64
67
  Best-effort detection of the *current* interactive shell.
@@ -99,19 +102,19 @@ def detect_shell() -> str:
99
102
 
100
103
 
101
104
  def _bash_like(name: str, value: str, unset: bool) -> str:
102
- return f'unset {name}' if unset else f'export {name}="{value}"'
105
+ return f"unset {name}" if unset else f'export {name}="{value}"'
103
106
 
104
107
 
105
108
  def _fish(name: str, value: str, unset: bool) -> str:
106
- return f'set -e {name}' if unset else f'set -gx {name} "{value}"'
109
+ return f"set -e {name}" if unset else f'set -gx {name} "{value}"'
107
110
 
108
111
 
109
112
  def _powershell(name: str, value: str, unset: bool) -> str:
110
- return f'Remove-Item Env:{name}' if unset else f'$Env:{name}="{value}"'
113
+ return f"Remove-Item Env:{name}" if unset else f'$Env:{name}="{value}"'
111
114
 
112
115
 
113
116
  def _cmd(name: str, value: str, unset: bool) -> str:
114
- return f'set {name}=' if unset else f'set {name}={value}'
117
+ return f"set {name}=" if unset else f"set {name}={value}"
115
118
 
116
119
 
117
120
  SHELL_RENDERERS = {
@@ -128,7 +131,8 @@ SHELL_RENDERERS = {
128
131
  help="Generate commands to set meshagent environment variables.",
129
132
  )
130
133
  def env(
131
- shell: Optional[str] = typer.Option(None,
134
+ shell: Optional[str] = typer.Option(
135
+ None,
132
136
  "--shell",
133
137
  case_sensitive=False,
134
138
  help="bash | zsh | fish | powershell | cmd",
@@ -138,7 +142,7 @@ def env(
138
142
  ),
139
143
  ):
140
144
  """Print shell-specific exports/unsets for Docker environment variables."""
141
-
145
+
142
146
  async def command():
143
147
  nonlocal shell, unset
144
148
  shell = (shell or detect_shell()).lower()
@@ -151,14 +155,18 @@ def env(
151
155
  project_id = await resolve_project_id(project_id=None)
152
156
  api_key_id = await resolve_api_key(project_id=project_id, api_key_id=None)
153
157
 
154
- token = (await client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
158
+ token = (
159
+ await client.decrypt_project_api_key(
160
+ project_id=project_id, id=api_key_id
161
+ )
162
+ )["token"]
155
163
  finally:
156
164
  await client.close()
157
165
 
158
166
  vars = {
159
- "MESHAGENT_PROJECT_ID" : project_id,
160
- "MESHAGENT_API_KEY" : api_key_id,
161
- "MESHAGENT_SECRET" : token
167
+ "MESHAGENT_PROJECT_ID": project_id,
168
+ "MESHAGENT_API_KEY": api_key_id,
169
+ "MESHAGENT_SECRET": token,
162
170
  }
163
171
  if shell not in SHELL_RENDERERS:
164
172
  typer.echo(f"Unsupported shell '{shell}'.", err=True)
@@ -171,17 +179,21 @@ def env(
171
179
 
172
180
  if not unset and shell in ("bash", "zsh"):
173
181
  typer.echo(
174
- '\n# Run this command to configure your current shell:\n'
175
- f'# eval "$(meshagent env)"'
182
+ "\n# Run this command to configure your current shell:\n"
183
+ '# eval "$(meshagent env)"'
176
184
  )
177
-
185
+
178
186
  _run_async(command())
179
187
 
188
+
180
189
  @app.command("setup")
181
190
  def setup_command():
182
191
  """Perform initial login and project/api key activation."""
183
192
 
184
193
  async def runner():
194
+ print("\n", flush=True)
195
+ tprint("MeshAgent", "tarty10")
196
+ print("\n", flush=True)
185
197
  await auth.login()
186
198
  print("Activate a project...")
187
199
  project_id = await projects.activate(None, interactive=True)
@@ -195,5 +207,6 @@ def setup_command():
195
207
 
196
208
  _run_async(runner())
197
209
 
210
+
198
211
  if __name__ == "__main__":
199
212
  app()