meshagent-tools 0.8.2__py3-none-any.whl → 0.19.5__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.
@@ -0,0 +1,361 @@
1
+ from .config import ToolkitConfig
2
+ from .tool import Tool
3
+ from .toolkit import ToolContext, ToolkitBuilder
4
+ from .hosting import RemoteToolkit, Toolkit
5
+ from typing import Literal, Optional
6
+ from meshagent.api.room_server_client import DataType, RoomClient
7
+
8
+
9
+ class ListTablesTool(Tool):
10
+ def __init__(self):
11
+ input_schema = {
12
+ "type": "object",
13
+ "required": [],
14
+ "additionalProperties": False,
15
+ "properties": {},
16
+ }
17
+
18
+ super().__init__(
19
+ name="list_tables",
20
+ title="list tables",
21
+ description="list the tables in the room",
22
+ input_schema=input_schema,
23
+ )
24
+
25
+ async def execute(self, context: ToolContext):
26
+ return {"tables": await context.room.database.list_tables()}
27
+
28
+
29
+ class InsertRowsTool(Tool):
30
+ def __init__(
31
+ self,
32
+ *,
33
+ table: str,
34
+ schema: dict[str, DataType],
35
+ namespace: Optional[list[str]] = None,
36
+ ):
37
+ self.table = table
38
+ self.namespace = namespace
39
+
40
+ input_schema = {
41
+ "type": "object",
42
+ "required": [],
43
+ "additionalProperties": False,
44
+ "properties": {},
45
+ }
46
+
47
+ for k, v in schema.items():
48
+ input_schema["required"].append(k)
49
+ input_schema["properties"][k] = v.to_json_schema()
50
+
51
+ super().__init__(
52
+ name=f"insert_{table}_rows",
53
+ title=f"insert {table} rows",
54
+ description=f"insert rows into the {table} table",
55
+ input_schema={
56
+ "type": "object",
57
+ "required": ["rows"],
58
+ "additionalProperties": False,
59
+ "properties": {"rows": {"type": "array", "items": input_schema}},
60
+ },
61
+ )
62
+
63
+ async def execute(self, context: ToolContext, *, rows):
64
+ await context.room.database.insert(
65
+ table=self.table, records=rows, namespace=self.namespace
66
+ )
67
+
68
+
69
+ class DeleteRowsTool(Tool):
70
+ def __init__(
71
+ self,
72
+ *,
73
+ table: str,
74
+ schema: dict[str, DataType],
75
+ namespace: Optional[list[str]] = None,
76
+ ):
77
+ self.table = table
78
+ self.namespace = namespace
79
+
80
+ input_schema = {
81
+ "type": "object",
82
+ "required": [],
83
+ "additionalProperties": False,
84
+ "properties": {},
85
+ }
86
+
87
+ for k, v in schema.items():
88
+ input_schema["required"].append(k)
89
+ input_schema["properties"][k] = v.to_json_schema()
90
+
91
+ super().__init__(
92
+ name=f"delete_{table}_rows",
93
+ title=f"delete {table} rows",
94
+ description=f"search {table} table for rows with the specified values (specify null for a column to exclude it from the search) and then delete them",
95
+ input_schema=input_schema,
96
+ )
97
+
98
+ async def execute(self, context: ToolContext, **values):
99
+ search = {}
100
+
101
+ for k, v in values.items():
102
+ if v is not None:
103
+ search[k] = v
104
+
105
+ await context.room.database.delete(
106
+ table=self.table,
107
+ where=search if len(search) > 0 else None,
108
+ namespace=self.namespace,
109
+ )
110
+ return {"ok": True}
111
+
112
+
113
+ class UpdateTool(Tool):
114
+ def __init__(
115
+ self,
116
+ *,
117
+ table: str,
118
+ schema: dict[str, DataType],
119
+ namespace: Optional[list[str]] = None,
120
+ ):
121
+ self.table = table
122
+ self.namespace = namespace
123
+
124
+ columns = ""
125
+
126
+ for k, v in schema.items():
127
+ columns += f"column {k} => {v.to_json()}"
128
+
129
+ values_schema = {
130
+ "type": "object",
131
+ "required": [],
132
+ "additionalProperties": False,
133
+ "properties": {},
134
+ }
135
+
136
+ for k, v in schema.items():
137
+ values_schema["required"].append(k)
138
+ values_schema["properties"][k] = v.to_json_schema()
139
+
140
+ input_schema = {
141
+ "type": "object",
142
+ "required": [
143
+ "where",
144
+ "values",
145
+ ],
146
+ "additionalProperties": False,
147
+ "properties": {
148
+ "where": {
149
+ "type": "string",
150
+ "description": f"a lance db compatible filter, columns are: {columns}",
151
+ },
152
+ "values": values_schema,
153
+ },
154
+ }
155
+
156
+ super().__init__(
157
+ name=f"update_{table}_rows",
158
+ title=f"update {table} rows",
159
+ description=f"update {table} table where rows match the specified filter (with a lancedb compatible filter)",
160
+ input_schema=input_schema,
161
+ )
162
+
163
+ async def execute(self, context: ToolContext, *, where: str, values: dict):
164
+ await context.room.database.update(
165
+ table=self.table, where=where, values=values, namespace=self.namespace
166
+ )
167
+
168
+ return {"ok": True}
169
+
170
+
171
+ class SearchTool(Tool):
172
+ def __init__(
173
+ self,
174
+ *,
175
+ table: str,
176
+ schema: dict[str, DataType],
177
+ namespace: Optional[list[str]] = None,
178
+ ):
179
+ self.table = table
180
+ self.namespace = namespace
181
+
182
+ input_schema = {
183
+ "type": "object",
184
+ "required": [],
185
+ "additionalProperties": False,
186
+ "properties": {},
187
+ }
188
+
189
+ for k, v in schema.items():
190
+ input_schema["required"].append(k)
191
+ input_schema["properties"][k] = v.to_json_schema()
192
+
193
+ super().__init__(
194
+ name=f"search_{table}",
195
+ title=f"search {table}",
196
+ description=f"search {table} table for rows with the specified values (specify null for a column to exclude it from the search)",
197
+ input_schema=input_schema,
198
+ )
199
+
200
+ async def execute(self, context: ToolContext, **values):
201
+ search = {}
202
+
203
+ for k, v in values.items():
204
+ if v is not None:
205
+ search[k] = v
206
+
207
+ return {
208
+ "rows": await context.room.database.search(
209
+ table=self.table,
210
+ where=search if len(search) > 0 else None,
211
+ namespace=self.namespace,
212
+ )
213
+ }
214
+
215
+
216
+ class AdvancedSearchTool(Tool):
217
+ def __init__(
218
+ self,
219
+ *,
220
+ table: str,
221
+ schema: dict[str, DataType],
222
+ namespace: Optional[list[str]] = None,
223
+ ):
224
+ self.table = table
225
+ self.namespace = namespace
226
+
227
+ columns = ""
228
+
229
+ for k, v in schema.items():
230
+ columns += f"column {k} => {v.to_json()}"
231
+
232
+ input_schema = {
233
+ "type": "object",
234
+ "required": ["where"],
235
+ "additionalProperties": False,
236
+ "properties": {
237
+ "where": {
238
+ "type": "string",
239
+ "description": f"a lance db compatible filter, columns are: {columns}",
240
+ }
241
+ },
242
+ }
243
+
244
+ super().__init__(
245
+ name=f"advanced_search_{table}",
246
+ title=f"advanced search {table}",
247
+ description=f"advanced search {table} table with a lancedb compatible filter",
248
+ input_schema=input_schema,
249
+ )
250
+
251
+ async def execute(self, context: ToolContext, *, where: str):
252
+ return {
253
+ "rows": await context.room.database.search(
254
+ table=self.table, where=where, namespace=self.namespace
255
+ )
256
+ }
257
+
258
+
259
+ class AdvancedDeleteRowsTool(Tool):
260
+ def __init__(
261
+ self,
262
+ *,
263
+ table: str,
264
+ schema: dict[str, DataType],
265
+ namespace: Optional[list[str]] = None,
266
+ ):
267
+ self.table = table
268
+ self.namespace = namespace
269
+ columns = ""
270
+
271
+ for k, v in schema.items():
272
+ columns += f"column {k} => {v.to_json()}"
273
+
274
+ input_schema = {
275
+ "type": "object",
276
+ "required": ["where"],
277
+ "additionalProperties": False,
278
+ "properties": {
279
+ "where": {
280
+ "type": "string",
281
+ "description": f"a lance db compatible filter, columns are: {columns}",
282
+ }
283
+ },
284
+ }
285
+
286
+ super().__init__(
287
+ name=f"advanced_delete_{table}",
288
+ title=f"advanced delete {table}",
289
+ description=f"advanced search {table} table with a lancedb compatible filter and delete the matching rows",
290
+ input_schema=input_schema,
291
+ )
292
+
293
+ async def execute(self, context: ToolContext, *, where: str):
294
+ await context.room.database.delete(
295
+ table=self.table, where=where, namespace=self.namespace
296
+ )
297
+ return {"ok": True}
298
+
299
+
300
+ class DatabaseToolkit(RemoteToolkit):
301
+ def __init__(
302
+ self,
303
+ *,
304
+ tables: dict[str, dict[str, DataType]],
305
+ read_only: bool = False,
306
+ namespace: Optional[list[str]] = None,
307
+ ):
308
+ tools = [
309
+ # ListTablesTool()
310
+ ]
311
+
312
+ for table, schema in tables.items():
313
+ if not read_only:
314
+ tools.append(
315
+ InsertRowsTool(table=table, schema=schema, namespace=namespace)
316
+ )
317
+ tools.append(
318
+ UpdateTool(table=table, schema=schema, namespace=namespace)
319
+ )
320
+ tools.append(
321
+ DeleteRowsTool(table=table, schema=schema, namespace=namespace)
322
+ )
323
+ tools.append(
324
+ AdvancedDeleteRowsTool(
325
+ table=table, schema=schema, namespace=namespace
326
+ )
327
+ )
328
+
329
+ tools.append(SearchTool(table=table, schema=schema, namespace=namespace))
330
+ tools.append(
331
+ AdvancedSearchTool(table=table, schema=schema, namespace=namespace)
332
+ )
333
+
334
+ super().__init__(
335
+ name="database",
336
+ title="database",
337
+ description="tools for interacting with meshagent databases",
338
+ tools=tools,
339
+ )
340
+
341
+
342
+ class DatabaseToolkitConfig(ToolkitConfig):
343
+ name: Literal["database"] = "database"
344
+ tables: list[str]
345
+ namespace: Optional[list[str]] = None
346
+ read_only: bool
347
+
348
+
349
+ class DatabaseToolkitBuilder(ToolkitBuilder):
350
+ def __init__(self):
351
+ super().__init__(name="database", type=DatabaseToolkitConfig)
352
+
353
+ async def make(
354
+ self, *, room: RoomClient, model: str, config: DatabaseToolkitConfig
355
+ ) -> Toolkit:
356
+ tables = {}
357
+ for t in config.tables:
358
+ tables[t] = await room.database.inspect(table=t, namespace=config.namespace)
359
+ return DatabaseToolkit(
360
+ tables=tables, read_only=config.read_only, namespace=config.namespace
361
+ )
@@ -1,4 +1,5 @@
1
1
  from meshagent.api.messaging import JsonResponse, LinkResponse, FileResponse
2
+ from meshagent.api import RoomClient
2
3
  from .config import ToolkitConfig
3
4
  from .tool import Tool
4
5
  from .toolkit import ToolContext, ToolkitBuilder
@@ -175,7 +176,7 @@ class SaveFileFromUrlTool(Tool):
175
176
 
176
177
  class StorageToolkit(RemoteToolkit):
177
178
  def __init__(self, read_only: bool = False):
178
- if read_only:
179
+ if not read_only:
179
180
  tools = [
180
181
  ListFilesTool(),
181
182
  WriteFileTool(),
@@ -196,12 +197,14 @@ class StorageToolkit(RemoteToolkit):
196
197
 
197
198
 
198
199
  class StorageToolkitConfig(ToolkitConfig):
199
- name: str = Literal["storage"]
200
+ name: Literal["storage"] = "storage"
200
201
 
201
202
 
202
203
  class StorageToolkitBuilder(ToolkitBuilder):
203
204
  def __init__(self):
204
205
  super().__init__(name="storage", type=StorageToolkitConfig)
205
206
 
206
- def make(self, *, model: str, config: ToolkitConfig) -> Toolkit:
207
+ async def make(
208
+ self, *, room: RoomClient, model: str, config: ToolkitConfig
209
+ ) -> Toolkit:
207
210
  return StorageToolkit()
meshagent/tools/tool.py CHANGED
@@ -3,7 +3,7 @@ from meshagent.api.participant import Participant
3
3
  import logging
4
4
  from abc import ABC
5
5
 
6
- from typing import Optional, Dict, Any
6
+ from typing import Optional, Dict, Any, Callable
7
7
 
8
8
  from meshagent.api.messaging import Response
9
9
 
@@ -23,11 +23,13 @@ class ToolContext:
23
23
  caller: Participant,
24
24
  on_behalf_of: Optional[Participant] = None,
25
25
  caller_context: Optional[Dict[str, Any]] = None,
26
+ event_handler: Optional[Callable[[dict], None]] = None,
26
27
  ):
27
28
  self._room = room
28
29
  self._caller = caller
29
30
  self._on_behalf_of = on_behalf_of
30
31
  self._caller_context = caller_context
32
+ self._event_handler = event_handler
31
33
 
32
34
  @property
33
35
  def caller(self) -> Participant:
@@ -45,6 +47,10 @@ class ToolContext:
45
47
  def caller_context(self) -> Dict[str, Any]:
46
48
  return self._caller_context
47
49
 
50
+ def emit(self, event: dict):
51
+ if self._event_handler is not None:
52
+ self._event_handler(event)
53
+
48
54
 
49
55
  class BaseTool(ABC):
50
56
  def __init__(
@@ -1,5 +1,6 @@
1
1
  from meshagent.api.room_server_client import RoomException
2
2
  from meshagent.api.messaging import ensure_response
3
+ from meshagent.api import RoomClient
3
4
  from jsonschema import validate
4
5
  import logging
5
6
 
@@ -23,7 +24,7 @@ class ToolkitConfig(ToolkitConfig):
23
24
 
24
25
  def make_basic_toolkit_config_cls(toolkit: "Toolkit"):
25
26
  class CustomToolkitConfig:
26
- name: Literal[toolkit.name]
27
+ name: Literal[toolkit.name] = toolkit.name
27
28
 
28
29
  return CustomToolkitConfig
29
30
 
@@ -33,7 +34,9 @@ class ToolkitBuilder:
33
34
  self.name = name
34
35
  self.type = type
35
36
 
36
- def make(self, *, model: str, config: ToolkitConfig) -> "Toolkit": ...
37
+ async def make(
38
+ self, *, room: RoomClient, model: str, config: ToolkitConfig
39
+ ) -> "Toolkit": ...
37
40
 
38
41
 
39
42
  class Toolkit(ToolkitBuilder):
@@ -94,12 +97,16 @@ class Toolkit(ToolkitBuilder):
94
97
  span.set_attribute("response_type", response.to_json()["type"])
95
98
  return response
96
99
 
97
- def make(self, *, model: str, config: ToolkitConfig):
100
+ async def make(self, *, room: RoomClient, model: str, config: ToolkitConfig):
98
101
  return self
99
102
 
100
103
 
101
- def make_toolkits(
102
- *, model: str, providers: list[ToolkitBuilder], tools: list[ToolkitConfig]
104
+ async def make_toolkits(
105
+ *,
106
+ room: RoomClient,
107
+ model: str,
108
+ providers: list[ToolkitBuilder],
109
+ tools: list[ToolkitConfig],
103
110
  ) -> list[Toolkit]:
104
111
  result = []
105
112
  if tools is not None:
@@ -109,14 +116,18 @@ def make_toolkits(
109
116
  for t in providers:
110
117
  if t.name == config["name"]:
111
118
  config = t.type.model_validate(config)
112
- result.append(t.make(model=model, config=config))
119
+ result.append(
120
+ await t.make(room=room, model=model, config=config)
121
+ )
113
122
  found = True
114
123
  break
115
124
 
116
125
  else:
117
126
  for t in providers:
118
127
  if t.type is type(config):
119
- result.append(t.make(model=model, config=config))
128
+ result.append(
129
+ await t.make(room=room, model=model, config=config)
130
+ )
120
131
  found = True
121
132
  break
122
133
 
@@ -1 +1 @@
1
- __version__ = "0.8.2"
1
+ __version__ = "0.19.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-tools
3
- Version: 0.8.2
3
+ Version: 0.19.5
4
4
  Summary: Tools for Meshagent
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Documentation, https://docs.meshagent.com
@@ -12,7 +12,7 @@ License-File: LICENSE
12
12
  Requires-Dist: pyjwt~=2.10
13
13
  Requires-Dist: pytest~=8.4
14
14
  Requires-Dist: pytest-asyncio~=0.26
15
- Requires-Dist: meshagent-api~=0.8.2
15
+ Requires-Dist: meshagent-api~=0.19.5
16
16
  Requires-Dist: aiohttp~=3.10
17
17
  Requires-Dist: opentelemetry-distro~=0.54b1
18
18
  Dynamic: license-file
@@ -0,0 +1,20 @@
1
+ meshagent/tools/__init__.py,sha256=1zgD5OvAJP10eERoE72VbDu9hFVfAqCWUXw3SiCYFTE,1285
2
+ meshagent/tools/blob.py,sha256=aDW_z8R4HrmrYzAWoWm13Ypqcxcl4rL1Dc0ESnQETLM,1473
3
+ meshagent/tools/config.py,sha256=zH2xGxg28K7Tg-aYor6LXdzf0LRxS9iE0679H1FuWhE,79
4
+ meshagent/tools/database.py,sha256=UMH0_qNZsWqHvI7NFXwWJUADbiOM5AnMYyi0gJtwM7U,10631
5
+ meshagent/tools/discovery.py,sha256=GZcC4XCgk0ftcYCBxuWlaIsLV5vU4-gXiu0HhlDUlwY,1861
6
+ meshagent/tools/document_tools.py,sha256=LMULXOSBjsvhKjqzxUxe8586t0Vol0v1Btu5v6ofm7A,11755
7
+ meshagent/tools/hosting.py,sha256=l1BCgnSrCJQsWU9Kycq3hEI4ZlYxffDfde6QeJUfko0,10678
8
+ meshagent/tools/multi_tool.py,sha256=hmWZO18Y2tuFG_7rvUed9er29aXleAC-r3YpXBCZWUY,4040
9
+ meshagent/tools/pydantic.py,sha256=n-MD0gC-oRtHSTUDD5IV2dP-xIk-zjcDgHfgjqMgiqM,1161
10
+ meshagent/tools/storage.py,sha256=Y79G__Mp4swJ3tnm-zXtD-SqDeKU9kqHEVoUQH_QedA,7212
11
+ meshagent/tools/strict_schema.py,sha256=IytdAANa6lsfrsg5FsJuqYrxH9D_fayl-Lc9EwgLJSM,6277
12
+ meshagent/tools/tool.py,sha256=9OAlbfaHqfgJnCDBSW-8PS0Z1K1KjWGD3JBUyiHOxAk,3131
13
+ meshagent/tools/toolkit.py,sha256=rCCkpQBoSkmmhjnRGA4jx0QP-ds6WTJ0PkQVnf1Ls7s,3843
14
+ meshagent/tools/version.py,sha256=HAeF5puNS6GWsF0P-GNaplZ6BOBhTmjj6k6KsmvJoyE,23
15
+ meshagent/tools/web_toolkit.py,sha256=IoOYjOBmcbQsqWT14xYg02jjWpWmGOkDSxt2U-LQoaA,1258
16
+ meshagent_tools-0.19.5.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
17
+ meshagent_tools-0.19.5.dist-info/METADATA,sha256=MPXlgC_FoRzHShCUW_EuGEAaThrTQLcFZcHG_GRbJqU,2878
18
+ meshagent_tools-0.19.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ meshagent_tools-0.19.5.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
20
+ meshagent_tools-0.19.5.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- meshagent/tools/__init__.py,sha256=1zgD5OvAJP10eERoE72VbDu9hFVfAqCWUXw3SiCYFTE,1285
2
- meshagent/tools/blob.py,sha256=aDW_z8R4HrmrYzAWoWm13Ypqcxcl4rL1Dc0ESnQETLM,1473
3
- meshagent/tools/config.py,sha256=zH2xGxg28K7Tg-aYor6LXdzf0LRxS9iE0679H1FuWhE,79
4
- meshagent/tools/discovery.py,sha256=GZcC4XCgk0ftcYCBxuWlaIsLV5vU4-gXiu0HhlDUlwY,1861
5
- meshagent/tools/document_tools.py,sha256=LMULXOSBjsvhKjqzxUxe8586t0Vol0v1Btu5v6ofm7A,11755
6
- meshagent/tools/hosting.py,sha256=l1BCgnSrCJQsWU9Kycq3hEI4ZlYxffDfde6QeJUfko0,10678
7
- meshagent/tools/multi_tool.py,sha256=hmWZO18Y2tuFG_7rvUed9er29aXleAC-r3YpXBCZWUY,4040
8
- meshagent/tools/pydantic.py,sha256=n-MD0gC-oRtHSTUDD5IV2dP-xIk-zjcDgHfgjqMgiqM,1161
9
- meshagent/tools/storage.py,sha256=eAajqVTk9g8djzefsb6T8bqaRz0XFazwip1BTK5Dumg,7127
10
- meshagent/tools/strict_schema.py,sha256=IytdAANa6lsfrsg5FsJuqYrxH9D_fayl-Lc9EwgLJSM,6277
11
- meshagent/tools/tool.py,sha256=Omig09KIC2UbWFClbQSfcUdmFbZ8NnQ64Joa6LfhYyM,2896
12
- meshagent/tools/toolkit.py,sha256=5-s7kddpZHYYltYWYYvb8-v7WANA1M2DeCZ-Qh-ZLOs,3546
13
- meshagent/tools/version.py,sha256=B7GiO0rd49YwtLYjvPg4lmCZEDlMTonslQKdSImaMJk,22
14
- meshagent/tools/web_toolkit.py,sha256=IoOYjOBmcbQsqWT14xYg02jjWpWmGOkDSxt2U-LQoaA,1258
15
- meshagent_tools-0.8.2.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
16
- meshagent_tools-0.8.2.dist-info/METADATA,sha256=ZKtaUauyubRuMfeB9fJGpVCSQPi1pN51uHzod67cnhg,2876
17
- meshagent_tools-0.8.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- meshagent_tools-0.8.2.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
19
- meshagent_tools-0.8.2.dist-info/RECORD,,