meshagent-tools 0.24.6__py3-none-any.whl → 0.25.1__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.
@@ -16,6 +16,7 @@ from .tool import (
16
16
  ToolContext,
17
17
  Tool,
18
18
  BaseTool,
19
+ tool,
19
20
  )
20
21
 
21
22
  from .config import ToolkitConfig
@@ -30,6 +31,19 @@ from .hosting import (
30
31
  )
31
32
  from .multi_tool import MultiTool, MultiToolkit
32
33
  from .version import __version__
34
+ from .web_toolkit import (
35
+ WebFetchConfig,
36
+ WebFetchTool,
37
+ WebFetchToolkitBuilder,
38
+ WebToolkit,
39
+ )
40
+ from .container_shell import (
41
+ ContainerShellToolConfig,
42
+ ContainerShellToolkitBuilder,
43
+ ContainerShellTool,
44
+ )
45
+
46
+ from .script import ScriptTool, ScriptToolConfig, ScriptToolkitBuilder
33
47
 
34
48
  from meshagent.api import websocket_protocol, RoomClient, ParticipantToken
35
49
  from meshagent.api.websocket_protocol import WebSocketClientProtocol
@@ -52,6 +66,7 @@ __all__ = [
52
66
  ToolContext,
53
67
  Toolkit,
54
68
  Response,
69
+ tool,
55
70
  LinkResponse,
56
71
  BaseTool,
57
72
  RemoteToolkit,
@@ -64,5 +79,15 @@ __all__ = [
64
79
  make_toolkits,
65
80
  ToolkitConfig,
66
81
  get_bytes_from_url,
82
+ WebFetchConfig,
83
+ WebFetchTool,
84
+ WebFetchToolkitBuilder,
85
+ WebToolkit,
86
+ ContainerShellToolConfig,
87
+ ContainerShellToolkitBuilder,
88
+ ContainerShellTool,
89
+ ScriptTool,
90
+ ScriptToolConfig,
91
+ ScriptToolkitBuilder,
67
92
  __version__,
68
93
  ]
meshagent/tools/blob.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from uuid import uuid4
2
2
  import base64
3
- import aiohttp
3
+ from meshagent.api.http import new_client_session
4
4
 
5
5
 
6
6
  class Blob:
@@ -48,7 +48,7 @@ async def get_bytes_from_url(*, url: str) -> Blob:
48
48
  # file_name = str(uuid.uuid4())+extension
49
49
  return Blob(mime_type=mime_type, data=content)
50
50
  else:
51
- async with aiohttp.ClientSession() as session:
51
+ async with new_client_session() as session:
52
52
  async with session.get(url=url) as response:
53
53
  content = await response.content.read()
54
54
  return Blob(mime_type=response.content_type, data=content)
@@ -0,0 +1,226 @@
1
+ from meshagent.api import RoomClient
2
+ from .tool import ToolContext, Tool
3
+ from .toolkit import Toolkit, ToolkitBuilder
4
+
5
+ from meshagent.api.specs.service import ContainerMountSpec, RoomStorageMountSpec
6
+ from typing import Literal, Optional
7
+ import os
8
+
9
+ import logging
10
+ import asyncio
11
+ from pydantic import BaseModel
12
+
13
+ logger = logging.getLogger("container_shell_tool")
14
+
15
+
16
+ DEFAULT_CONTAINER_MOUNT_SPEC = ContainerMountSpec(
17
+ room=[RoomStorageMountSpec(path="/data")]
18
+ )
19
+
20
+
21
+ class ContainerShellToolConfig(BaseModel):
22
+ name: Literal["container_shell"] = "container_shell"
23
+
24
+
25
+ class ContainerShellToolkitBuilder(ToolkitBuilder):
26
+ def __init__(
27
+ self,
28
+ *,
29
+ name: str = "container_shell",
30
+ working_directory: Optional[str] = None,
31
+ image: Optional[str] = "python:3.13",
32
+ mounts: Optional[ContainerMountSpec] = DEFAULT_CONTAINER_MOUNT_SPEC,
33
+ env: Optional[dict[str, str]] = None,
34
+ ):
35
+ super().__init__(name=name, type=ContainerShellToolConfig)
36
+
37
+ self.working_directory = working_directory
38
+ self.image = image
39
+ self.mounts = mounts
40
+ self.env = env
41
+
42
+ async def make(
43
+ self, *, room: RoomClient, model: str, config: ContainerShellToolConfig
44
+ ) -> Toolkit:
45
+ return Toolkit(
46
+ name=self.name,
47
+ tools=[
48
+ ContainerShellTool(
49
+ name=self.name,
50
+ working_directory=self.working_directory,
51
+ image=self.image,
52
+ mounts=self.mounts,
53
+ env=self.env,
54
+ )
55
+ ],
56
+ )
57
+
58
+
59
+ class ContainerShellTool(Tool):
60
+ def __init__(
61
+ self,
62
+ *,
63
+ name: str = "container_shell",
64
+ description: Optional[str] = None,
65
+ title: Optional[str] = None,
66
+ working_directory: Optional[str] = None,
67
+ image: Optional[str] = "python:3.13",
68
+ mounts: Optional[ContainerMountSpec] = DEFAULT_CONTAINER_MOUNT_SPEC,
69
+ env: Optional[dict[str, str]] = None,
70
+ ):
71
+ self.working_directory = working_directory
72
+ self.image = image
73
+ self.mounts = mounts
74
+ self._container_id = None
75
+ self.env = env
76
+
77
+ super().__init__(
78
+ name=name,
79
+ description=description
80
+ or "execute shell commands in a container and return the result",
81
+ title=title,
82
+ input_schema={
83
+ "type": "object",
84
+ "required": ["commands"],
85
+ "additionalProperties": False,
86
+ "properties": {
87
+ "commands": {"type": "array", "items": {"type": "string"}},
88
+ "max_output_length": {"type": "integer"},
89
+ "timeout_ms": {"type": "integer"},
90
+ },
91
+ },
92
+ )
93
+
94
+ async def execute(
95
+ self,
96
+ context: ToolContext,
97
+ **kwargs,
98
+ ):
99
+ commands = kwargs.get("commands") or []
100
+ max_output_length = kwargs.get("max_output_length")
101
+ timeout_ms = kwargs.get("timeout_ms")
102
+
103
+ if not commands:
104
+ raise Exception("commands is required")
105
+
106
+ if self.image is None:
107
+ raise Exception("container_shell requires an image")
108
+
109
+ results = []
110
+ encoding = os.device_encoding(1) or "utf-8"
111
+
112
+ left = max_output_length
113
+
114
+ def limit(s: str):
115
+ nonlocal left
116
+ if left is not None:
117
+ s = s[0:left]
118
+ left -= len(s)
119
+ return s
120
+ else:
121
+ return s
122
+
123
+ timeout = float(timeout_ms) / 1000.0 if timeout_ms else 20 * 1000.0
124
+
125
+ running = False
126
+
127
+ if self._container_id:
128
+ for c in await context.room.containers.list():
129
+ if c.id == self._container_id:
130
+ running = True
131
+
132
+ if not running:
133
+ self._container_id = await context.room.containers.run(
134
+ command="sleep infinity",
135
+ image=self.image,
136
+ mounts=self.mounts,
137
+ writable_root_fs=True,
138
+ env=self.env,
139
+ )
140
+
141
+ container_id = self._container_id
142
+
143
+ try:
144
+ logger.info(
145
+ "executing shell commands in container %s with timeout %s: %s",
146
+ container_id,
147
+ timeout,
148
+ commands,
149
+ )
150
+ import shlex
151
+
152
+ for command in commands:
153
+ command_to_run = command
154
+ if self.working_directory:
155
+ command_to_run = (
156
+ f"cd {shlex.quote(self.working_directory)} && {command}"
157
+ )
158
+ exec = await context.room.containers.exec(
159
+ container_id=container_id,
160
+ command=shlex.join(["bash", "-lc", command_to_run]),
161
+ tty=False,
162
+ )
163
+
164
+ stdout = bytearray()
165
+ stderr = bytearray()
166
+
167
+ try:
168
+ async with asyncio.timeout(timeout):
169
+ async for se in exec.stderr():
170
+ stderr.extend(se)
171
+
172
+ async for so in exec.stdout():
173
+ stdout.extend(so)
174
+
175
+ exit_code = await exec.result
176
+
177
+ results.append(
178
+ {
179
+ "outcome": {
180
+ "type": "exit",
181
+ "exit_code": exit_code,
182
+ },
183
+ "stdout": stdout.decode(),
184
+ "stderr": stderr.decode(),
185
+ }
186
+ )
187
+
188
+ except asyncio.TimeoutError:
189
+ logger.info("The command timed out after %ss", timeout)
190
+ await exec.kill()
191
+
192
+ results.append(
193
+ {
194
+ "outcome": {"type": "timeout"},
195
+ "stdout": limit(stdout.decode(encoding, errors="replace")),
196
+ "stderr": limit(stderr.decode(encoding, errors="replace")),
197
+ }
198
+ )
199
+ break
200
+
201
+ except Exception as ex:
202
+ results.append(
203
+ {
204
+ "outcome": {
205
+ "type": "exit",
206
+ "exit_code": 1,
207
+ },
208
+ "stdout": "",
209
+ "stderr": f"{ex}",
210
+ }
211
+ )
212
+ break
213
+
214
+ except Exception as ex:
215
+ results.append(
216
+ {
217
+ "outcome": {
218
+ "type": "exit",
219
+ "exit_code": 1,
220
+ },
221
+ "stdout": "",
222
+ "stderr": f"{ex}",
223
+ }
224
+ )
225
+
226
+ return {"results": results}
@@ -128,7 +128,7 @@ class UpdateTool(Tool):
128
128
  columns = ""
129
129
 
130
130
  for k, v in schema.items():
131
- columns += f"column {k} => {v.to_json()}"
131
+ columns += f"column {k} => {v.model_dump(mode='json')}"
132
132
 
133
133
  anyOf = []
134
134
 
@@ -500,7 +500,7 @@ class AdvancedSearchTool(Tool):
500
500
  columns = ""
501
501
 
502
502
  for k, v in schema.items():
503
- columns += f"column {k} => {v.to_json()}\n"
503
+ columns += f"column {k} => {v.model_dump(mode='json')}\n"
504
504
 
505
505
  input_schema = {
506
506
  "type": "object",
@@ -542,7 +542,7 @@ class AdvancedDeleteRowsTool(Tool):
542
542
  columns = ""
543
543
 
544
544
  for k, v in schema.items():
545
- columns += f"column {k} => {v.to_json()}"
545
+ columns += f"column {k} => {v.model_dump(mode='json')}"
546
546
 
547
547
  input_schema = {
548
548
  "type": "object",
meshagent/tools/script.py CHANGED
@@ -1,9 +1,7 @@
1
1
  from meshagent.api import RoomClient
2
- from meshagent.tools import Toolkit, ToolContext, Tool
2
+ from meshagent.tools.tool import Tool, ToolContext
3
+ from meshagent.tools.toolkit import Toolkit, ToolkitBuilder
3
4
 
4
- from meshagent.agents.adapter import (
5
- ToolkitBuilder,
6
- )
7
5
 
8
6
  from meshagent.api.specs.service import ContainerMountSpec, RoomStorageMountSpec
9
7
  from typing import Literal
@@ -311,3 +309,39 @@ class ScriptTool(Tool):
311
309
  )
312
310
 
313
311
  return {"results": results}
312
+
313
+
314
+ async def get_script_tools(room: RoomClient):
315
+ services = await room.services.list()
316
+
317
+ st = []
318
+
319
+ for service in services:
320
+ if service.metadata.annotations is not None:
321
+ print("X")
322
+ type = service.metadata.annotations.get("meshagent.tool.type")
323
+ print(type)
324
+ commands_str = service.metadata.annotations.get("meshagent.tool.commands")
325
+ print(commands_str)
326
+ tool_name = service.metadata.annotations.get(
327
+ "meshagent.tool.name", service.metadata.name
328
+ )
329
+ description = service.metadata.annotations.get(
330
+ "meshagent.tool.description", service.metadata.description
331
+ )
332
+
333
+ if type == "script" and tool_name is not None:
334
+ print("FOUND")
335
+ if commands_str is not None:
336
+ commands = commands_str.split("\n")
337
+
338
+ st.append(
339
+ ScriptTool(
340
+ name=tool_name,
341
+ description=description,
342
+ service_id=service.id,
343
+ commands=commands,
344
+ )
345
+ )
346
+
347
+ return st
@@ -0,0 +1,45 @@
1
+ import pytest
2
+ from pydantic import BaseModel
3
+
4
+ from meshagent.api.messaging import JsonResponse
5
+ from meshagent.tools import Toolkit, ToolContext, tool
6
+
7
+
8
+ class Payload(BaseModel):
9
+ name: str
10
+ count: int
11
+
12
+
13
+ class Result(BaseModel):
14
+ name: str
15
+ count: int
16
+ flag: bool
17
+
18
+
19
+ @tool(name="make_payload")
20
+ async def make_payload(context: ToolContext, payload: Payload, flag: bool):
21
+ return Result(name=payload.name, count=payload.count, flag=flag)
22
+
23
+
24
+ @pytest.mark.asyncio
25
+ async def test_decorated_tool_executes_with_toolkit():
26
+ toolkit = Toolkit(name="test", tools=[make_payload])
27
+ context = ToolContext(room=object(), caller=object())
28
+
29
+ result = await toolkit.execute(
30
+ context=context,
31
+ name="make_payload",
32
+ arguments={"payload": {"name": "alpha", "count": 2}, "flag": True},
33
+ )
34
+
35
+ assert isinstance(result, JsonResponse)
36
+ assert result.json == {"name": "alpha", "count": 2, "flag": True}
37
+
38
+
39
+ def test_decorator_schema_is_strict():
40
+ schema = make_payload.input_schema
41
+
42
+ assert schema["type"] == "object"
43
+ assert schema["additionalProperties"] is False
44
+ assert "payload" in schema["properties"]
45
+ assert "flag" in schema["properties"]
meshagent/tools/tool.py CHANGED
@@ -3,9 +3,15 @@ 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, Callable
6
+ from typing import Optional, Dict, Any, Callable, get_type_hints
7
7
 
8
- from meshagent.api.messaging import Response
8
+ import inspect
9
+
10
+ from pydantic import BaseModel, create_model
11
+
12
+ from meshagent.tools.strict_schema import ensure_strict_json_schema
13
+
14
+ from meshagent.api.messaging import Response, ensure_response
9
15
 
10
16
 
11
17
  from opentelemetry import trace
@@ -117,5 +123,84 @@ class Tool(BaseTool):
117
123
  if defs is not None:
118
124
  openai_schema["$defs"] = {**defs}
119
125
 
126
+ async def invoke(
127
+ self, context: ToolContext, arguments: dict, attachment: Optional[bytes] = None
128
+ ) -> Response:
129
+ return await self.execute(context=context, **arguments)
130
+
120
131
  async def execute(self, context: ToolContext, **kwargs) -> Response:
121
132
  raise (Exception("Not implemented"))
133
+
134
+
135
+ def tool(
136
+ *,
137
+ name: Optional[str] = None,
138
+ title: Optional[str] = None,
139
+ description: Optional[str] = None,
140
+ rules: Optional[list[str]] = None,
141
+ thumbnail_url: Optional[str] = None,
142
+ ):
143
+ def decorator(fn: Callable[..., Response]):
144
+ signature = inspect.signature(fn)
145
+ hints = get_type_hints(fn, include_extras=True)
146
+
147
+ supports_context = False
148
+ fields: dict[str, tuple[Any, Any]] = {}
149
+
150
+ for param_name, param in signature.parameters.items():
151
+ annotation = hints.get(param_name, Any)
152
+ if annotation is ToolContext:
153
+ supports_context = True
154
+ continue
155
+
156
+ default = param.default if param.default is not inspect._empty else ...
157
+ fields[param_name] = (annotation, default)
158
+
159
+ InputModel = create_model(f"{fn.__name__}Input", **fields)
160
+ schema = InputModel.model_json_schema()
161
+ strict_schema = ensure_strict_json_schema(schema)
162
+
163
+ tool_name = name or fn.__name__
164
+ tool_title = title or tool_name
165
+ tool_description = (
166
+ description if description is not None else (fn.__doc__ or "").strip()
167
+ )
168
+
169
+ class FunctionTool(Tool):
170
+ def __init__(self):
171
+ super().__init__(
172
+ name=tool_name,
173
+ title=tool_title,
174
+ description=tool_description,
175
+ rules=rules,
176
+ thumbnail_url=thumbnail_url,
177
+ input_schema=strict_schema,
178
+ supports_context=supports_context,
179
+ )
180
+ self.strict = True
181
+
182
+ async def invoke(
183
+ self,
184
+ context: ToolContext,
185
+ arguments: dict,
186
+ attachment: Optional[bytes] = None,
187
+ ) -> Response:
188
+ data = InputModel.model_validate(arguments)
189
+ parsed_args = {field: getattr(data, field) for field in fields}
190
+
191
+ if supports_context:
192
+ result = fn(context, **parsed_args)
193
+ else:
194
+ result = fn(**parsed_args)
195
+
196
+ if inspect.isawaitable(result):
197
+ result = await result
198
+
199
+ if isinstance(result, BaseModel):
200
+ result = result.model_dump(mode="json")
201
+
202
+ return ensure_response(result)
203
+
204
+ return FunctionTool()
205
+
206
+ return decorator
@@ -8,7 +8,7 @@ import json
8
8
 
9
9
  from typing import Optional, Literal
10
10
  from meshagent.tools.config import ToolkitConfig
11
- from meshagent.tools.tool import ToolContext, BaseTool
11
+ from meshagent.tools.tool import ToolContext, BaseTool, Tool
12
12
 
13
13
  from opentelemetry import trace
14
14
 
@@ -91,7 +91,12 @@ class Toolkit(ToolkitBuilder):
91
91
  schema["$defs"] = {**tool.defs}
92
92
 
93
93
  validate(arguments, schema)
94
- response = await tool.execute(context=context, **arguments)
94
+ if isinstance(tool, Tool):
95
+ response = await tool.invoke(
96
+ context=context, arguments=arguments, attachment=attachment
97
+ )
98
+ else:
99
+ raise RoomException("tools must extend the Tool class to be invokable")
95
100
  response = ensure_response(response)
96
101
 
97
102
  span.set_attribute("response_type", response.to_json()["type"])
@@ -1 +1 @@
1
- __version__ = "0.24.6"
1
+ __version__ = "0.25.1"
@@ -1,19 +1,29 @@
1
- from meshagent.api.messaging import TextResponse
2
- from meshagent.tools import Toolkit, Tool, ToolContext
3
- from aiohttp import ClientSession
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import mimetypes
5
+ import os
6
+ from urllib.parse import urlparse
7
+
8
+ from meshagent.api.http import new_client_session
9
+ from meshagent.api.messaging import FileResponse, JsonResponse, Response, TextResponse
10
+ from meshagent.tools.config import ToolkitConfig
11
+ from meshagent.tools.tool import Tool, ToolContext
12
+ from meshagent.tools.toolkit import Toolkit, ToolkitBuilder
13
+ from meshagent.api.room_server_client import RoomClient
4
14
 
5
15
 
6
16
  class WebToolkit(Toolkit):
7
17
  def __init__(self):
8
- super().__init__(tools=[WebFetchTool()])
18
+ super().__init__(name="web_fetch", tools=[WebFetchTool()])
9
19
 
10
20
 
11
21
  class WebFetchTool(Tool):
12
22
  def __init__(self):
13
23
  super().__init__(
14
- name="get_web_page_text",
15
- title="get web page text",
16
- description="gets the text of a web page",
24
+ name="web_fetch",
25
+ title="web fetch",
26
+ description="fetches a url and returns text, json, or file content",
17
27
  input_schema={
18
28
  "type": "object",
19
29
  "properties": {
@@ -22,21 +32,104 @@ class WebFetchTool(Tool):
22
32
  "description": "the url of the web page (always start it with a proper scheme like https://)",
23
33
  }
24
34
  },
35
+ "required": ["url"],
25
36
  "additionalProperties": False,
26
37
  },
27
38
  )
28
39
 
29
- async def execute(
30
- self,
31
- *,
32
- context: ToolContext,
33
- url: str,
34
- ) -> str:
35
- async with ClientSession() as session:
40
+ async def execute(self, context: ToolContext, **kwargs: object) -> Response:
41
+ url = str(kwargs.get("url", ""))
42
+ if not url:
43
+ raise ValueError("url is required")
44
+ async with new_client_session() as session:
36
45
  async with session.get(url) as resp:
37
- body = await resp.text()
46
+ if resp.status >= 400:
47
+ raise Exception(f"web fetch failed with status {resp.status}")
48
+
49
+ content_type = (resp.content_type or "").lower()
50
+ data = await resp.read()
51
+
52
+ if _is_json_content_type(content_type):
53
+ text = _decode_text(data=data, charset=resp.charset)
54
+ try:
55
+ parsed = json.loads(text)
56
+ except json.JSONDecodeError:
57
+ return TextResponse(text=text)
58
+
59
+ if isinstance(parsed, dict):
60
+ return JsonResponse(json=parsed)
61
+ return JsonResponse(json={"data": parsed})
62
+
63
+ if _is_text_content_type(content_type):
64
+ text = _decode_text(data=data, charset=resp.charset)
65
+ if content_type == "text/html":
66
+ from html_to_markdown import convert
67
+
68
+ text = convert(text)
69
+ return TextResponse(text=text)
70
+
71
+ if _is_file_content_type(content_type):
72
+ filename = _infer_filename(url=url, content_type=content_type)
73
+ return FileResponse(
74
+ name=filename,
75
+ mime_type=content_type or "application/octet-stream",
76
+ data=data,
77
+ )
78
+
79
+ filename = _infer_filename(url=url, content_type=content_type)
80
+ return FileResponse(
81
+ name=filename,
82
+ mime_type=content_type or "application/octet-stream",
83
+ data=data,
84
+ )
85
+
86
+
87
+ def _decode_text(*, data: bytes, charset: str | None) -> str:
88
+ encoding = charset or "utf-8"
89
+ return data.decode(encoding, errors="replace")
90
+
38
91
 
39
- from bs4 import BeautifulSoup
92
+ def _is_json_content_type(content_type: str) -> bool:
93
+ if content_type in {"application/json", "text/json"}:
94
+ return True
95
+ return content_type.endswith("+json")
96
+
97
+
98
+ def _is_text_content_type(content_type: str) -> bool:
99
+ if content_type.startswith("text/"):
100
+ return True
101
+ return content_type in {
102
+ "application/xml",
103
+ "application/xhtml+xml",
104
+ "application/javascript",
105
+ "application/x-javascript",
106
+ }
107
+
108
+
109
+ def _is_file_content_type(content_type: str) -> bool:
110
+ if content_type.startswith("image/"):
111
+ return True
112
+ return content_type == "application/pdf"
113
+
114
+
115
+ def _infer_filename(*, url: str, content_type: str) -> str:
116
+ parsed = urlparse(url)
117
+ basename = os.path.basename(parsed.path)
118
+ if basename:
119
+ return basename
120
+ extension = mimetypes.guess_extension(content_type or "") or ""
121
+ return f"downloaded-content{extension}"
122
+
123
+
124
+ class WebFetchConfig(ToolkitConfig):
125
+ name: str = "web_fetch"
126
+
127
+
128
+ class WebFetchToolkitBuilder(ToolkitBuilder):
129
+ def __init__(self):
130
+ super().__init__(name="web_fetch", type=WebFetchConfig)
40
131
 
41
- soup = BeautifulSoup(body, "html.parser")
42
- return TextResponse(text=soup.get_text())
132
+ async def make(
133
+ self, *, room: RoomClient, model: str, config: WebFetchConfig
134
+ ) -> Toolkit:
135
+ return WebToolkit()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-tools
3
- Version: 0.24.6
3
+ Version: 0.25.1
4
4
  Summary: Tools for Meshagent
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Documentation, https://docs.meshagent.com
@@ -12,8 +12,9 @@ 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.24.6
16
- Requires-Dist: aiohttp~=3.10
15
+ Requires-Dist: meshagent-api~=0.25.1
16
+ Requires-Dist: html-to-markdown~=2.24.3
17
+ Requires-Dist: aiohttp[speedups]~=3.13.0
17
18
  Requires-Dist: opentelemetry-distro~=0.54b1
18
19
  Dynamic: license-file
19
20
 
@@ -0,0 +1,25 @@
1
+ meshagent/tools/__init__.py,sha256=2qAAi4eUVq4q6gXyAGnjQze2Rs9Q9kIJfdm4oEheRPQ,1843
2
+ meshagent/tools/blob.py,sha256=Liw1zdtDc-Viy6Hcgrb4XZ8ULUPEubzShWAdIci0wWI,1505
3
+ meshagent/tools/config.py,sha256=zH2xGxg28K7Tg-aYor6LXdzf0LRxS9iE0679H1FuWhE,79
4
+ meshagent/tools/container_shell.py,sha256=BMcTi_j0-yUPk9ZkVux1j8CxDFmyrCYFn-XeT3S77Ik,7056
5
+ meshagent/tools/database.py,sha256=HWH7_Fm_8GexDJv_T-D6o0yJVUPmE-j31TU2AfcgaPs,18438
6
+ meshagent/tools/datetime.py,sha256=2pOUOWopYIsc5y4EoFo_1PdBaBcTSkeOOs_EqdqYTk0,17503
7
+ meshagent/tools/discovery.py,sha256=f7DJtwIiBQCxByTepsvGM2NRn-9KGxZTZMoTRCKYQ7E,1251
8
+ meshagent/tools/document_tools.py,sha256=LMULXOSBjsvhKjqzxUxe8586t0Vol0v1Btu5v6ofm7A,11755
9
+ meshagent/tools/hosting.py,sha256=l1BCgnSrCJQsWU9Kycq3hEI4ZlYxffDfde6QeJUfko0,10678
10
+ meshagent/tools/multi_tool.py,sha256=hmWZO18Y2tuFG_7rvUed9er29aXleAC-r3YpXBCZWUY,4040
11
+ meshagent/tools/pydantic.py,sha256=n-MD0gC-oRtHSTUDD5IV2dP-xIk-zjcDgHfgjqMgiqM,1161
12
+ meshagent/tools/script.py,sha256=eyQiufoc2ZkTUBTO58VQURnkkQA2lboED5s0-BvAvgM,11811
13
+ meshagent/tools/storage.py,sha256=NVpi9CZKSZUh8PTxxCdJhJy7Gzmdp55-zo2yHYGod_E,23340
14
+ meshagent/tools/strict_schema.py,sha256=IytdAANa6lsfrsg5FsJuqYrxH9D_fayl-Lc9EwgLJSM,6277
15
+ meshagent/tools/tool.py,sha256=HgvlOlz2wMrmD5aaV49fRpnGyXJwnVcH9j4wKaaPbWo,5935
16
+ meshagent/tools/toolkit.py,sha256=iVcCvhwWqmahDow9FN-VRWGo9MEj669Vw7TAB7Mx9Ww,4066
17
+ meshagent/tools/uuid.py,sha256=mzRwDmXy39U5lHhd9wqV4r-ZdS8jPfDTTs4UfW4KHJQ,1342
18
+ meshagent/tools/version.py,sha256=ACu2Z3Q3TFgYpAno_eu9ssJ1QULjNXvjGvyqDSHrQ_o,23
19
+ meshagent/tools/web_toolkit.py,sha256=Seju8gpdUoPku7Yfar_s-cVOnlweFzKj-bFqrQVup8o,4603
20
+ meshagent/tools/tests/tool_decorator_test.py,sha256=Fd4uvoefU8hpdSWaiYen15tqnlwoY092G6hjYafaMGE,1190
21
+ meshagent_tools-0.25.1.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
22
+ meshagent_tools-0.25.1.dist-info/METADATA,sha256=I_DrA3zW7uRwNXwllW8OSipjUKQimC_BgOP4_8xoDBI,2930
23
+ meshagent_tools-0.25.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
24
+ meshagent_tools-0.25.1.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
25
+ meshagent_tools-0.25.1.dist-info/RECORD,,
@@ -1,23 +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/database.py,sha256=Jd_YJLPIBsypSK1SziwaYb8xKnG4oafoCDs6vzAC2Oc,18396
5
- meshagent/tools/datetime.py,sha256=2pOUOWopYIsc5y4EoFo_1PdBaBcTSkeOOs_EqdqYTk0,17503
6
- meshagent/tools/discovery.py,sha256=f7DJtwIiBQCxByTepsvGM2NRn-9KGxZTZMoTRCKYQ7E,1251
7
- meshagent/tools/document_tools.py,sha256=LMULXOSBjsvhKjqzxUxe8586t0Vol0v1Btu5v6ofm7A,11755
8
- meshagent/tools/hosting.py,sha256=l1BCgnSrCJQsWU9Kycq3hEI4ZlYxffDfde6QeJUfko0,10678
9
- meshagent/tools/multi_tool.py,sha256=hmWZO18Y2tuFG_7rvUed9er29aXleAC-r3YpXBCZWUY,4040
10
- meshagent/tools/pydantic.py,sha256=n-MD0gC-oRtHSTUDD5IV2dP-xIk-zjcDgHfgjqMgiqM,1161
11
- meshagent/tools/script.py,sha256=uHrJynzM0SwUHM1qXIjt-UhZLG4AQtFw-yyLv4lxGGw,10589
12
- meshagent/tools/storage.py,sha256=NVpi9CZKSZUh8PTxxCdJhJy7Gzmdp55-zo2yHYGod_E,23340
13
- meshagent/tools/strict_schema.py,sha256=IytdAANa6lsfrsg5FsJuqYrxH9D_fayl-Lc9EwgLJSM,6277
14
- meshagent/tools/tool.py,sha256=9OAlbfaHqfgJnCDBSW-8PS0Z1K1KjWGD3JBUyiHOxAk,3131
15
- meshagent/tools/toolkit.py,sha256=rCCkpQBoSkmmhjnRGA4jx0QP-ds6WTJ0PkQVnf1Ls7s,3843
16
- meshagent/tools/uuid.py,sha256=mzRwDmXy39U5lHhd9wqV4r-ZdS8jPfDTTs4UfW4KHJQ,1342
17
- meshagent/tools/version.py,sha256=1LERjg6TTlEZCKHKtH82Ox1j8xI8WMA94hEjeTPs1Io,23
18
- meshagent/tools/web_toolkit.py,sha256=IoOYjOBmcbQsqWT14xYg02jjWpWmGOkDSxt2U-LQoaA,1258
19
- meshagent_tools-0.24.6.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
20
- meshagent_tools-0.24.6.dist-info/METADATA,sha256=AzHewWZ3JwdinUT2J0eNarP9N_IPR-GF2fKN13myULI,2878
21
- meshagent_tools-0.24.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
22
- meshagent_tools-0.24.6.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
23
- meshagent_tools-0.24.6.dist-info/RECORD,,