meshagent-cli 0.21.0__py3-none-any.whl → 0.23.0__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.
meshagent/cli/chatbot.py CHANGED
@@ -1,8 +1,8 @@
1
1
  import typer
2
2
  from rich import print
3
- from typing import Annotated, Optional
4
- from meshagent.tools import Toolkit
5
- from meshagent.tools.storage import StorageToolkitBuilder
3
+ from typing import Annotated, Optional, List
4
+ from meshagent.tools import Toolkit, ToolkitConfig
5
+ from meshagent.tools.storage import StorageToolkitBuilder, StorageToolkitConfig
6
6
  from meshagent.tools.datetime import DatetimeToolkit
7
7
  from meshagent.tools.uuid import UUIDToolkit
8
8
  from meshagent.tools.document_tools import (
@@ -35,8 +35,8 @@ from meshagent.cli.helper import (
35
35
  )
36
36
 
37
37
  from meshagent.openai import OpenAIResponsesAdapter
38
+ from meshagent.anthropic import AnthropicOpenAIResponsesStreamAdapter
38
39
 
39
- from typing import List
40
40
  from pathlib import Path
41
41
 
42
42
  from meshagent.openai.tools.responses_adapter import (
@@ -74,6 +74,9 @@ import yaml
74
74
  import shlex
75
75
  import sys
76
76
 
77
+ import asyncio
78
+
79
+
77
80
  from meshagent.api.client import ConflictError
78
81
 
79
82
  logger = logging.getLogger("chatbot")
@@ -84,7 +87,6 @@ app = async_typer.AsyncTyper(help="Join a chatbot to a room")
84
87
  def build_chatbot(
85
88
  *,
86
89
  model: str,
87
- agent_name: str,
88
90
  rule: List[str],
89
91
  toolkit: List[str],
90
92
  schema: List[str],
@@ -120,6 +122,7 @@ def build_chatbot(
120
122
  skill_dirs: Optional[list[str]] = None,
121
123
  shell_image: Optional[str] = None,
122
124
  log_llm_requests: Optional[bool] = None,
125
+ delegate_shell_token: Optional[bool] = None,
123
126
  ):
124
127
  from meshagent.agents.chat import ChatBot
125
128
 
@@ -148,6 +151,7 @@ def build_chatbot(
148
151
  print(f"[yellow]rules file not found at {rules_file}[/yellow]")
149
152
 
150
153
  BaseClass = ChatBot
154
+ decision_model = None
151
155
  if llm_participant:
152
156
  llm_adapter = MessageStreamLLMAdapter(
153
157
  participant_name=llm_participant,
@@ -163,22 +167,29 @@ def build_chatbot(
163
167
  log_requests=log_llm_requests,
164
168
  )
165
169
  else:
166
- llm_adapter = OpenAIResponsesAdapter(
167
- model=model,
168
- log_requests=log_llm_requests,
169
- )
170
+ if model.startswith("claude-"):
171
+ llm_adapter = AnthropicOpenAIResponsesStreamAdapter(
172
+ model=model,
173
+ log_requests=log_llm_requests,
174
+ )
175
+ decision_model = model
176
+ else:
177
+ llm_adapter = OpenAIResponsesAdapter(
178
+ model=model,
179
+ log_requests=log_llm_requests,
180
+ )
170
181
 
171
182
  class CustomChatbot(BaseClass):
172
183
  def __init__(self):
173
184
  super().__init__(
174
185
  llm_adapter=llm_adapter,
175
- name=agent_name,
176
186
  requires=requirements,
177
187
  toolkits=toolkits,
178
188
  rules=rule if len(rule) > 0 else None,
179
189
  client_rules=client_rules,
180
190
  always_reply=always_reply,
181
191
  skill_dirs=skill_dirs,
192
+ decision_model=decision_model,
182
193
  )
183
194
 
184
195
  async def start(self, *, room: RoomClient):
@@ -188,6 +199,14 @@ def build_chatbot(
188
199
  for p in room_rules_path:
189
200
  await self._load_room_rules(path=p)
190
201
 
202
+ async def init_chat_context(self):
203
+ from meshagent.cli.helper import init_context_from_spec
204
+
205
+ context = await super().init_chat_context()
206
+ await init_context_from_spec(context)
207
+
208
+ return context
209
+
191
210
  async def _load_room_rules(
192
211
  self,
193
212
  *,
@@ -268,12 +287,18 @@ def build_chatbot(
268
287
  )
269
288
  )
270
289
 
290
+ env = {}
291
+
292
+ if delegate_shell_token:
293
+ env["MESHAGENT_TOKEN"] = self.room.protocol.token
294
+
271
295
  if require_shell:
272
296
  providers.append(
273
297
  ShellTool(
274
298
  working_directory=working_directory,
275
299
  config=ShellConfig(name="shell"),
276
300
  image=shell_image or "python:3.13",
301
+ env=env,
277
302
  )
278
303
  )
279
304
 
@@ -419,12 +444,14 @@ def build_chatbot(
419
444
 
420
445
 
421
446
  @app.async_command("join")
422
- async def make_call(
447
+ async def join(
423
448
  *,
424
449
  project_id: ProjectIdOption,
425
450
  room: RoomOption,
426
451
  role: str = "agent",
427
- agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
452
+ agent_name: Annotated[
453
+ Optional[str], typer.Option(..., help="Name of the agent to call")
454
+ ] = None,
428
455
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
429
456
  room_rules: Annotated[
430
457
  List[str],
@@ -584,6 +611,10 @@ async def make_call(
584
611
  Optional[str],
585
612
  typer.Option(..., help="an image tag to use to run shell commands in"),
586
613
  ] = None,
614
+ delegate_shell_token: Annotated[
615
+ Optional[bool],
616
+ typer.Option(..., help="log all requests to the llm"),
617
+ ] = False,
587
618
  log_llm_requests: Annotated[
588
619
  Optional[bool],
589
620
  typer.Option(..., help="log all requests to the llm"),
@@ -598,76 +629,92 @@ async def make_call(
598
629
  project_id = await resolve_project_id(project_id=project_id)
599
630
  room = resolve_room(room)
600
631
 
601
- token = ParticipantToken(
602
- name=agent_name,
603
- )
632
+ jwt = os.getenv("MESHAGENT_TOKEN")
633
+ if jwt is None:
634
+ if agent_name is None:
635
+ print(
636
+ "[bold red]--agent-name must be specified when the MESHAGENT_TOKEN environment variable is not set[/bold red]"
637
+ )
638
+ raise typer.Exit(1)
604
639
 
605
- token.add_api_grant(ApiScope.agent_default(tunnels=require_computer_use))
640
+ token = ParticipantToken(
641
+ name=agent_name,
642
+ )
606
643
 
607
- token.add_role_grant(role=role)
608
- token.add_room_grant(room)
644
+ token.add_api_grant(ApiScope.agent_default(tunnels=require_computer_use))
609
645
 
610
- jwt = token.to_jwt(api_key=key)
646
+ token.add_role_grant(role=role)
647
+ token.add_room_grant(room)
648
+
649
+ jwt = token.to_jwt(api_key=key)
611
650
 
612
651
  print("[bold green]Connecting to room...[/bold green]", flush=True)
613
- async with RoomClient(
614
- protocol=WebSocketClientProtocol(
615
- url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
616
- token=jwt,
617
- )
618
- ) as client:
619
- CustomChatbot = build_chatbot(
620
- computer_use=computer_use,
621
- require_computer_use=require_computer_use,
622
- model=model,
623
- agent_name=agent_name,
624
- rule=rule,
625
- toolkit=require_toolkit + toolkit,
626
- schema=require_schema + schema,
627
- rules_file=rules_file,
628
- local_shell=local_shell,
629
- shell=shell,
630
- apply_patch=apply_patch,
631
- image_generation=image_generation,
632
- web_search=web_search,
633
- mcp=mcp,
634
- storage=storage,
635
- require_apply_patch=require_apply_patch,
636
- require_web_search=require_web_search,
637
- require_local_shell=require_local_shell,
638
- require_shell=require_shell,
639
- require_image_generation=require_image_generation,
640
- require_mcp=require_mcp,
641
- require_storage=require_storage,
642
- require_table_read=require_table_read,
643
- require_table_write=require_table_write,
644
- require_read_only_storage=require_read_only_storage,
645
- require_time=require_time,
646
- require_uuid=require_uuid,
647
- room_rules_path=room_rules,
648
- require_document_authoring=require_document_authoring,
649
- require_discovery=require_discovery,
650
- working_directory=working_directory,
651
- llm_participant=llm_participant,
652
- always_reply=always_reply,
653
- database_namespace=database_namespace,
654
- skill_dirs=skill_dir,
655
- shell_image=shell_image,
656
- log_llm_requests=log_llm_requests,
657
- )
658
652
 
659
- bot = CustomChatbot()
653
+ CustomChatbot = build_chatbot(
654
+ computer_use=computer_use,
655
+ require_computer_use=require_computer_use,
656
+ model=model,
657
+ rule=rule,
658
+ toolkit=require_toolkit + toolkit,
659
+ schema=require_schema + schema,
660
+ rules_file=rules_file,
661
+ local_shell=local_shell,
662
+ shell=shell,
663
+ apply_patch=apply_patch,
664
+ image_generation=image_generation,
665
+ web_search=web_search,
666
+ mcp=mcp,
667
+ storage=storage,
668
+ require_apply_patch=require_apply_patch,
669
+ require_web_search=require_web_search,
670
+ require_local_shell=require_local_shell,
671
+ require_shell=require_shell,
672
+ require_image_generation=require_image_generation,
673
+ require_mcp=require_mcp,
674
+ require_storage=require_storage,
675
+ require_table_read=require_table_read,
676
+ require_table_write=require_table_write,
677
+ require_read_only_storage=require_read_only_storage,
678
+ require_time=require_time,
679
+ require_uuid=require_uuid,
680
+ room_rules_path=room_rules,
681
+ require_document_authoring=require_document_authoring,
682
+ require_discovery=require_discovery,
683
+ working_directory=working_directory,
684
+ llm_participant=llm_participant,
685
+ always_reply=always_reply,
686
+ database_namespace=database_namespace,
687
+ skill_dirs=skill_dir,
688
+ shell_image=shell_image,
689
+ delegate_shell_token=delegate_shell_token,
690
+ log_llm_requests=log_llm_requests,
691
+ )
660
692
 
661
- await bot.start(room=client)
662
- try:
663
- print(
664
- 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]",
665
- flush=True,
693
+ bot = CustomChatbot()
694
+
695
+ if get_deferred():
696
+ from meshagent.cli.host import agents
697
+
698
+ agents.append((bot, jwt))
699
+ else:
700
+ async with RoomClient(
701
+ protocol=WebSocketClientProtocol(
702
+ url=websocket_room_url(
703
+ room_name=room, base_url=meshagent_base_url()
704
+ ),
705
+ token=jwt,
666
706
  )
667
- await client.protocol.wait_for_close()
668
- except KeyboardInterrupt:
669
- await bot.stop()
707
+ ) as client:
708
+ await bot.start(room=client)
709
+ try:
710
+ print(
711
+ 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]",
712
+ flush=True,
713
+ )
714
+ await client.protocol.wait_for_close()
670
715
 
716
+ except KeyboardInterrupt:
717
+ await bot.stop()
671
718
  finally:
672
719
  await account_client.close()
673
720
 
@@ -839,6 +886,10 @@ async def service(
839
886
  Optional[str],
840
887
  typer.Option(..., help="an image tag to use to run shell commands in"),
841
888
  ] = None,
889
+ delegate_shell_token: Annotated[
890
+ Optional[bool],
891
+ typer.Option(..., help="log all requests to the llm"),
892
+ ] = False,
842
893
  log_llm_requests: Annotated[
843
894
  Optional[bool],
844
895
  typer.Option(..., help="log all requests to the llm"),
@@ -870,7 +921,6 @@ async def service(
870
921
  local_shell=local_shell,
871
922
  shell=shell,
872
923
  apply_patch=apply_patch,
873
- agent_name=agent_name,
874
924
  rule=rule,
875
925
  toolkit=require_toolkit + toolkit,
876
926
  schema=require_schema + schema,
@@ -900,6 +950,7 @@ async def service(
900
950
  always_reply=always_reply,
901
951
  skill_dirs=skill_dir,
902
952
  shell_image=shell_image,
953
+ delegate_shell_token=delegate_shell_token,
903
954
  log_llm_requests=log_llm_requests,
904
955
  ),
905
956
  )
@@ -1083,6 +1134,10 @@ async def spec(
1083
1134
  Optional[str],
1084
1135
  typer.Option(..., help="an image tag to use to run shell commands in"),
1085
1136
  ] = None,
1137
+ delegate_shell_token: Annotated[
1138
+ Optional[bool],
1139
+ typer.Option(..., help="log all requests to the llm"),
1140
+ ] = False,
1086
1141
  log_llm_requests: Annotated[
1087
1142
  Optional[bool],
1088
1143
  typer.Option(..., help="log all requests to the llm"),
@@ -1114,7 +1169,6 @@ async def spec(
1114
1169
  local_shell=local_shell,
1115
1170
  shell=shell,
1116
1171
  apply_patch=apply_patch,
1117
- agent_name=agent_name,
1118
1172
  rule=rule,
1119
1173
  toolkit=require_toolkit + toolkit,
1120
1174
  schema=require_schema + schema,
@@ -1144,6 +1198,7 @@ async def spec(
1144
1198
  always_reply=always_reply,
1145
1199
  skill_dirs=skill_dir,
1146
1200
  shell_image=shell_image,
1201
+ delegate_shell_token=delegate_shell_token,
1147
1202
  log_llm_requests=log_llm_requests,
1148
1203
  ),
1149
1204
  )
@@ -1340,6 +1395,10 @@ async def deploy(
1340
1395
  Optional[str],
1341
1396
  typer.Option(..., help="an image tag to use to run shell commands in"),
1342
1397
  ] = None,
1398
+ delegate_shell_token: Annotated[
1399
+ Optional[bool],
1400
+ typer.Option(..., help="log all requests to the llm"),
1401
+ ] = False,
1343
1402
  log_llm_requests: Annotated[
1344
1403
  Optional[bool],
1345
1404
  typer.Option(..., help="log all requests to the llm"),
@@ -1378,7 +1437,6 @@ async def deploy(
1378
1437
  local_shell=local_shell,
1379
1438
  shell=shell,
1380
1439
  apply_patch=apply_patch,
1381
- agent_name=agent_name,
1382
1440
  rule=rule,
1383
1441
  toolkit=require_toolkit + toolkit,
1384
1442
  schema=require_schema + schema,
@@ -1408,12 +1466,16 @@ async def deploy(
1408
1466
  always_reply=always_reply,
1409
1467
  skill_dirs=skill_dir,
1410
1468
  shell_image=shell_image,
1469
+ delegate_shell_token=delegate_shell_token,
1411
1470
  log_llm_requests=log_llm_requests,
1412
1471
  ),
1413
1472
  )
1414
1473
 
1415
1474
  spec = service_specs()[0]
1416
1475
 
1476
+ for port in spec.ports:
1477
+ port
1478
+
1417
1479
  spec.metadata.annotations = {
1418
1480
  "meshagent.service.id": service_name,
1419
1481
  }
@@ -1477,3 +1539,445 @@ async def deploy(
1477
1539
 
1478
1540
  finally:
1479
1541
  await client.close()
1542
+
1543
+
1544
+ async def chat_with(
1545
+ *,
1546
+ participant_name: str,
1547
+ project_id: str,
1548
+ room: str,
1549
+ thread_path: str,
1550
+ message: Optional[str] = None,
1551
+ use_web_search: bool = False,
1552
+ use_image_gen: bool = False,
1553
+ use_storage: bool = False,
1554
+ ):
1555
+ from prompt_toolkit.shortcuts import PromptSession
1556
+ from prompt_toolkit.key_binding import KeyBindings
1557
+ from meshagent.agents.chat import ChatBotClient
1558
+
1559
+ kb = KeyBindings()
1560
+
1561
+ session = PromptSession("> ", key_bindings=kb)
1562
+
1563
+ account_client = await get_client()
1564
+ try:
1565
+ connection = await account_client.connect_room(project_id=project_id, room=room)
1566
+ async with RoomClient(
1567
+ protocol=WebSocketClientProtocol(
1568
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
1569
+ token=connection.jwt,
1570
+ ),
1571
+ ) as user_client:
1572
+ await user_client.messaging.enable()
1573
+
1574
+ if thread_path is None:
1575
+ thread_path = f".threads/{participant_name}/{user_client.local_participant.get_attribute('name')}.thread"
1576
+
1577
+ async with ChatBotClient(
1578
+ room=user_client,
1579
+ participant_name=participant_name,
1580
+ thread_path=thread_path,
1581
+ ) as chat_client:
1582
+
1583
+ @kb.add("c-l")
1584
+ def _(event):
1585
+ event.app.renderer.clear()
1586
+ asyncio.ensure_future(chat_client.clear())
1587
+
1588
+ while True:
1589
+ user_input = message or await session.prompt_async()
1590
+
1591
+ if user_input == "/clear":
1592
+ await chat_client.clear()
1593
+
1594
+ else:
1595
+ tools: list[ToolkitConfig] = []
1596
+
1597
+ if use_web_search:
1598
+ tools.append(WebSearchConfig())
1599
+
1600
+ elif use_image_gen:
1601
+ tools.append(ImageGenerationConfig())
1602
+
1603
+ elif use_storage:
1604
+ tools.append(StorageToolkitConfig())
1605
+
1606
+ await chat_client.send(text=user_input, tools=tools)
1607
+
1608
+ response = await chat_client.receive()
1609
+
1610
+ print(response)
1611
+
1612
+ if message:
1613
+ break
1614
+
1615
+ except asyncio.CancelledError:
1616
+ pass
1617
+
1618
+ finally:
1619
+ await account_client.close()
1620
+
1621
+
1622
+ @app.async_command("run")
1623
+ async def run(
1624
+ *,
1625
+ project_id: ProjectIdOption,
1626
+ room: RoomOption,
1627
+ role: str = "agent",
1628
+ agent_name: Annotated[
1629
+ Optional[str], typer.Option(..., help="Name of the agent to call")
1630
+ ] = None,
1631
+ rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
1632
+ room_rules: Annotated[
1633
+ List[str],
1634
+ typer.Option(
1635
+ "--room-rules",
1636
+ "-rr",
1637
+ help="a path to a rules file within the room that can be used to customize the agent's behavior",
1638
+ ),
1639
+ ] = [],
1640
+ rules_file: Optional[str] = None,
1641
+ require_toolkit: Annotated[
1642
+ List[str],
1643
+ typer.Option(
1644
+ "--require-toolkit", "-rt", help="the name or url of a required toolkit"
1645
+ ),
1646
+ ] = [],
1647
+ require_schema: Annotated[
1648
+ List[str],
1649
+ typer.Option(
1650
+ "--require-schema", "-rs", help="the name or url of a required schema"
1651
+ ),
1652
+ ] = [],
1653
+ toolkit: Annotated[
1654
+ List[str],
1655
+ typer.Option(
1656
+ "--toolkit", "-t", help="the name or url of a required toolkit", hidden=True
1657
+ ),
1658
+ ] = [],
1659
+ schema: Annotated[
1660
+ List[str],
1661
+ typer.Option(
1662
+ "--schema", "-s", help="the name or url of a required schema", hidden=True
1663
+ ),
1664
+ ] = [],
1665
+ model: Annotated[
1666
+ str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
1667
+ ] = "gpt-5.2",
1668
+ image_generation: Annotated[
1669
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1670
+ ] = None,
1671
+ computer_use: Annotated[
1672
+ Optional[bool],
1673
+ typer.Option(
1674
+ ..., help="Enable computer use (requires computer-use-preview model)"
1675
+ ),
1676
+ ] = False,
1677
+ local_shell: Annotated[
1678
+ Optional[bool], typer.Option(..., help="Enable local shell tool calling")
1679
+ ] = False,
1680
+ shell: Annotated[
1681
+ Optional[bool], typer.Option(..., help="Enable function shell tool calling")
1682
+ ] = False,
1683
+ apply_patch: Annotated[
1684
+ Optional[bool], typer.Option(..., help="Enable apply patch tool")
1685
+ ] = False,
1686
+ web_search: Annotated[
1687
+ Optional[bool], typer.Option(..., help="Enable web search tool calling")
1688
+ ] = False,
1689
+ mcp: Annotated[
1690
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1691
+ ] = False,
1692
+ storage: Annotated[
1693
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1694
+ ] = False,
1695
+ require_image_generation: Annotated[
1696
+ Optional[str], typer.Option(..., help="Name of an image gen model")
1697
+ ] = None,
1698
+ require_computer_use: Annotated[
1699
+ Optional[bool],
1700
+ typer.Option(
1701
+ ...,
1702
+ help="Enable computer use (requires computer-use-preview model)",
1703
+ hidden=True,
1704
+ ),
1705
+ ] = False,
1706
+ require_local_shell: Annotated[
1707
+ Optional[bool],
1708
+ typer.Option(..., help="Enable local shell tool calling"),
1709
+ ] = False,
1710
+ require_shell: Annotated[
1711
+ Optional[bool],
1712
+ typer.Option(..., help="Enable function shell tool calling"),
1713
+ ] = False,
1714
+ require_apply_patch: Annotated[
1715
+ Optional[bool],
1716
+ typer.Option(..., help="Enable apply patch tool calling"),
1717
+ ] = False,
1718
+ require_web_search: Annotated[
1719
+ Optional[bool],
1720
+ typer.Option(..., help="Enable web search tool calling"),
1721
+ ] = False,
1722
+ require_mcp: Annotated[
1723
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
1724
+ ] = False,
1725
+ require_storage: Annotated[
1726
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
1727
+ ] = False,
1728
+ database_namespace: Annotated[
1729
+ Optional[str],
1730
+ typer.Option(..., help="Use a specific database namespace"),
1731
+ ] = None,
1732
+ require_table_read: Annotated[
1733
+ list[str],
1734
+ typer.Option(..., help="Enable table read tools for a specific table"),
1735
+ ] = [],
1736
+ require_table_write: Annotated[
1737
+ list[str],
1738
+ typer.Option(..., help="Enable table write tools for a specific table"),
1739
+ ] = [],
1740
+ require_read_only_storage: Annotated[
1741
+ Optional[bool],
1742
+ typer.Option(..., help="Enable read only storage toolkit"),
1743
+ ] = False,
1744
+ require_time: Annotated[
1745
+ bool,
1746
+ typer.Option(
1747
+ ...,
1748
+ help="Enable time/datetime tools",
1749
+ ),
1750
+ ] = True,
1751
+ require_uuid: Annotated[
1752
+ bool,
1753
+ typer.Option(
1754
+ ...,
1755
+ help="Enable UUID generation tools",
1756
+ ),
1757
+ ] = False,
1758
+ require_document_authoring: Annotated[
1759
+ Optional[bool],
1760
+ typer.Option(..., help="Enable MeshDocument authoring"),
1761
+ ] = False,
1762
+ require_discovery: Annotated[
1763
+ Optional[bool],
1764
+ typer.Option(..., help="Enable discovery of agents and tools"),
1765
+ ] = False,
1766
+ working_directory: Annotated[
1767
+ Optional[str],
1768
+ typer.Option(..., help="The default working directory for shell commands"),
1769
+ ] = None,
1770
+ key: Annotated[
1771
+ str,
1772
+ typer.Option("--key", help="an api key to sign the token with"),
1773
+ ] = None,
1774
+ llm_participant: Annotated[
1775
+ Optional[str],
1776
+ typer.Option(..., help="Delegate LLM interactions to a remote participant"),
1777
+ ] = None,
1778
+ always_reply: Annotated[
1779
+ Optional[bool],
1780
+ typer.Option(..., help="Always reply"),
1781
+ ] = None,
1782
+ skill_dir: Annotated[
1783
+ list[str],
1784
+ typer.Option(..., help="an agent skills directory"),
1785
+ ] = [],
1786
+ shell_image: Annotated[
1787
+ Optional[str],
1788
+ typer.Option(..., help="an image tag to use to run shell commands in"),
1789
+ ] = None,
1790
+ delegate_shell_token: Annotated[
1791
+ Optional[bool],
1792
+ typer.Option(..., help="log all requests to the llm"),
1793
+ ] = False,
1794
+ log_llm_requests: Annotated[
1795
+ Optional[bool],
1796
+ typer.Option(..., help="log all requests to the llm"),
1797
+ ] = False,
1798
+ thread_path: Annotated[
1799
+ Optional[str],
1800
+ typer.Option(..., help="log all requests to the llm"),
1801
+ ] = None,
1802
+ message: Annotated[
1803
+ Optional[str],
1804
+ typer.Option(..., help="the input message to use"),
1805
+ ] = None,
1806
+ use_web_search: Annotated[
1807
+ Optional[bool],
1808
+ typer.Option(..., help="request the web search tool"),
1809
+ ] = None,
1810
+ use_image_gen: Annotated[
1811
+ Optional[bool],
1812
+ typer.Option(..., help="request the image gen tool"),
1813
+ ] = None,
1814
+ use_storage: Annotated[
1815
+ Optional[bool],
1816
+ typer.Option(..., help="request the storage tool"),
1817
+ ] = None,
1818
+ ):
1819
+ root = logging.getLogger()
1820
+ root.setLevel(logging.ERROR)
1821
+
1822
+ if database_namespace is not None:
1823
+ database_namespace = database_namespace.split("::")
1824
+
1825
+ key = await resolve_key(project_id=project_id, key=key)
1826
+ account_client = await get_client()
1827
+ try:
1828
+ project_id = await resolve_project_id(project_id=project_id)
1829
+ room = resolve_room(room)
1830
+
1831
+ jwt = os.getenv("MESHAGENT_TOKEN")
1832
+ if jwt is None:
1833
+ if agent_name is None:
1834
+ print(
1835
+ "[bold red]--agent-name must be specified when the MESHAGENT_TOKEN environment variable is not set[/bold red]"
1836
+ )
1837
+ raise typer.Exit(1)
1838
+
1839
+ token = ParticipantToken(
1840
+ name=agent_name,
1841
+ )
1842
+
1843
+ token.add_api_grant(ApiScope.agent_default(tunnels=require_computer_use))
1844
+
1845
+ token.add_role_grant(role=role)
1846
+ token.add_room_grant(room)
1847
+
1848
+ jwt = token.to_jwt(api_key=key)
1849
+
1850
+ async with RoomClient(
1851
+ protocol=WebSocketClientProtocol(
1852
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
1853
+ token=jwt,
1854
+ )
1855
+ ) as client:
1856
+ CustomChatbot = build_chatbot(
1857
+ computer_use=computer_use,
1858
+ require_computer_use=require_computer_use,
1859
+ model=model,
1860
+ rule=rule,
1861
+ toolkit=require_toolkit + toolkit,
1862
+ schema=require_schema + schema,
1863
+ rules_file=rules_file,
1864
+ local_shell=local_shell,
1865
+ shell=shell,
1866
+ apply_patch=apply_patch,
1867
+ image_generation=image_generation,
1868
+ web_search=web_search,
1869
+ mcp=mcp,
1870
+ storage=storage,
1871
+ require_apply_patch=require_apply_patch,
1872
+ require_web_search=require_web_search,
1873
+ require_local_shell=require_local_shell,
1874
+ require_shell=require_shell,
1875
+ require_image_generation=require_image_generation,
1876
+ require_mcp=require_mcp,
1877
+ require_storage=require_storage,
1878
+ require_table_read=require_table_read,
1879
+ require_table_write=require_table_write,
1880
+ require_read_only_storage=require_read_only_storage,
1881
+ require_time=require_time,
1882
+ require_uuid=require_uuid,
1883
+ room_rules_path=room_rules,
1884
+ require_document_authoring=require_document_authoring,
1885
+ require_discovery=require_discovery,
1886
+ working_directory=working_directory,
1887
+ llm_participant=llm_participant,
1888
+ always_reply=always_reply,
1889
+ database_namespace=database_namespace,
1890
+ skill_dirs=skill_dir,
1891
+ shell_image=shell_image,
1892
+ delegate_shell_token=delegate_shell_token,
1893
+ log_llm_requests=log_llm_requests,
1894
+ )
1895
+
1896
+ bot = CustomChatbot()
1897
+
1898
+ await bot.start(room=client)
1899
+
1900
+ _, pending = await asyncio.wait(
1901
+ [
1902
+ asyncio.create_task(client.protocol.wait_for_close()),
1903
+ asyncio.create_task(
1904
+ chat_with(
1905
+ participant_name=client.local_participant.get_attribute(
1906
+ "name"
1907
+ ),
1908
+ room=room,
1909
+ project_id=project_id,
1910
+ thread_path=thread_path,
1911
+ message=message,
1912
+ use_web_search=use_web_search,
1913
+ use_image_gen=use_image_gen,
1914
+ use_storage=use_storage,
1915
+ )
1916
+ ),
1917
+ ],
1918
+ return_when="FIRST_COMPLETED",
1919
+ )
1920
+
1921
+ for t in pending:
1922
+ t.cancel()
1923
+
1924
+ except asyncio.CancelledError:
1925
+ return
1926
+
1927
+ finally:
1928
+ await account_client.close()
1929
+
1930
+
1931
+ @app.async_command("use")
1932
+ async def use(
1933
+ *,
1934
+ project_id: ProjectIdOption,
1935
+ room: RoomOption,
1936
+ agent_name: Annotated[
1937
+ Optional[str], typer.Option(..., help="Name of the agent to call")
1938
+ ] = None,
1939
+ thread_path: Annotated[
1940
+ Optional[str],
1941
+ typer.Option(..., help="log all requests to the llm"),
1942
+ ] = None,
1943
+ message: Annotated[
1944
+ Optional[str],
1945
+ typer.Option(..., help="the input message to use"),
1946
+ ] = None,
1947
+ use_web_search: Annotated[
1948
+ Optional[bool],
1949
+ typer.Option(..., help="request the web search tool"),
1950
+ ] = None,
1951
+ use_image_gen: Annotated[
1952
+ Optional[bool],
1953
+ typer.Option(..., help="request the image gen tool"),
1954
+ ] = None,
1955
+ use_storage: Annotated[
1956
+ Optional[bool],
1957
+ typer.Option(..., help="request the storage tool"),
1958
+ ] = None,
1959
+ ):
1960
+ root = logging.getLogger()
1961
+ root.setLevel(logging.ERROR)
1962
+
1963
+ account_client = await get_client()
1964
+ try:
1965
+ project_id = await resolve_project_id(project_id=project_id)
1966
+ room = resolve_room(room)
1967
+
1968
+ await chat_with(
1969
+ participant_name=agent_name,
1970
+ room=room,
1971
+ project_id=project_id,
1972
+ thread_path=thread_path,
1973
+ message=message,
1974
+ use_web_search=use_web_search,
1975
+ use_image_gen=use_image_gen,
1976
+ use_storage=use_storage,
1977
+ )
1978
+
1979
+ except asyncio.CancelledError:
1980
+ return
1981
+
1982
+ finally:
1983
+ await account_client.close()