xpander-sdk 2.0.179__py3-none-any.whl → 2.0.180__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.
xpander_sdk/__init__.py CHANGED
@@ -28,6 +28,7 @@ from .modules.tasks.tasks_module import Tasks, Task, TasksListItem, AgentExecuti
28
28
  from xpander_sdk.modules.events.decorators.on_task import on_task
29
29
  from xpander_sdk.modules.events.decorators.on_boot import on_boot
30
30
  from xpander_sdk.modules.events.decorators.on_shutdown import on_shutdown
31
+ from xpander_sdk.modules.events.decorators.on_tool import on_tool_before, on_tool_after, on_tool_error
31
32
 
32
33
  # Tools and repository imports
33
34
  from .modules.tools_repository.tools_repository_module import ToolsRepository, Tool
@@ -77,6 +78,9 @@ __all__ = [
77
78
  "MCPServerAuthType",
78
79
  "register_tool",
79
80
  "build_model_from_schema",
81
+ "on_tool_before",
82
+ "on_tool_after",
83
+ "on_tool_error",
80
84
  # Knowledge bases
81
85
  "KnowledgeBases",
82
86
  "KnowledgeBase",
@@ -0,0 +1,3 @@
1
+ from .on_tool import on_tool_before, on_tool_after, on_tool_error
2
+
3
+ __all__ = ["on_tool_before", "on_tool_after", "on_tool_error"]
@@ -0,0 +1,384 @@
1
+ """
2
+ xpander_sdk.decorators.on_tool
3
+
4
+ This module provides decorators for tool invocation lifecycle hooks:
5
+ - `@on_tool_before`: Execute before tool invocation
6
+ - `@on_tool_after`: Execute after successful tool invocation
7
+ - `@on_tool_error`: Execute when tool invocation fails
8
+
9
+ The decorators ensure that registered functions:
10
+ - Accept parameters: Tool, payload, payload_extension, tool_call_id, agent_version
11
+ - Can be either synchronous or asynchronous
12
+ - Are called at the appropriate lifecycle stage during tool execution
13
+
14
+ Execution Notes:
15
+ - Before-hooks execute before tool invocation and can perform validation or logging
16
+ - After-hooks execute after successful invocation with access to the result
17
+ - Error-hooks execute when an exception occurs during invocation
18
+ - Multiple hooks of the same type can be registered and will execute in registration order
19
+ - Exceptions in hooks are logged but don't prevent tool execution
20
+
21
+ Example usage:
22
+ --------------
23
+ >>> @on_tool_before
24
+ ... async def log_tool_invocation(tool, payload, payload_extension, tool_call_id, agent_version):
25
+ ... logger.info(f"Invoking tool {tool.name} with payload: {payload}")
26
+
27
+ >>> @on_tool_after
28
+ ... def record_tool_result(tool, payload, payload_extension, tool_call_id, agent_version, result):
29
+ ... logger.info(f"Tool {tool.name} completed with result: {result}")
30
+
31
+ >>> @on_tool_error
32
+ ... async def handle_tool_error(tool, payload, payload_extension, tool_call_id, agent_version, error):
33
+ ... logger.error(f"Tool {tool.name} failed: {error}")
34
+ """
35
+
36
+ import asyncio
37
+ from functools import wraps
38
+ from inspect import iscoroutinefunction
39
+ from typing import Optional, Callable, Any, Dict, List
40
+
41
+ from xpander_sdk.models.configuration import Configuration
42
+
43
+
44
+ class ToolHooksRegistry:
45
+ """
46
+ Registry for tool invocation lifecycle hooks.
47
+
48
+ This class maintains class-level lists of hooks that are executed at different
49
+ stages of tool invocation: before, after, and on error.
50
+ """
51
+
52
+ _before_hooks: List[Callable] = []
53
+ _after_hooks: List[Callable] = []
54
+ _error_hooks: List[Callable] = []
55
+
56
+ @classmethod
57
+ def register_before_hook(cls, hook: Callable) -> None:
58
+ """
59
+ Register a before-invocation hook.
60
+
61
+ Args:
62
+ hook (Callable): The hook function to execute before tool invocation.
63
+ """
64
+ cls._before_hooks.append(hook)
65
+
66
+ @classmethod
67
+ def register_after_hook(cls, hook: Callable) -> None:
68
+ """
69
+ Register an after-invocation hook.
70
+
71
+ Args:
72
+ hook (Callable): The hook function to execute after successful tool invocation.
73
+ """
74
+ cls._after_hooks.append(hook)
75
+
76
+ @classmethod
77
+ def register_error_hook(cls, hook: Callable) -> None:
78
+ """
79
+ Register an error hook.
80
+
81
+ Args:
82
+ hook (Callable): The hook function to execute when tool invocation fails.
83
+ """
84
+ cls._error_hooks.append(hook)
85
+
86
+ @classmethod
87
+ async def execute_before_hooks(
88
+ cls,
89
+ tool: Any,
90
+ payload: Any,
91
+ payload_extension: Optional[Dict[str, Any]] = None,
92
+ tool_call_id: Optional[str] = None,
93
+ agent_version: Optional[str] = None
94
+ ) -> None:
95
+ """
96
+ Execute all registered before-invocation hooks.
97
+
98
+ Args:
99
+ tool: The Tool object being invoked.
100
+ payload: The payload being sent to the tool.
101
+ payload_extension: Additional payload data.
102
+ tool_call_id: Unique ID of the tool call.
103
+ agent_version: Version of the agent making the call.
104
+ """
105
+ from loguru import logger
106
+
107
+ for hook in cls._before_hooks:
108
+ try:
109
+ if asyncio.iscoroutinefunction(hook):
110
+ await hook(tool, payload, payload_extension, tool_call_id, agent_version)
111
+ else:
112
+ hook(tool, payload, payload_extension, tool_call_id, agent_version)
113
+ except Exception as e:
114
+ logger.error(f"Before-hook {hook.__name__} failed: {e}")
115
+
116
+ @classmethod
117
+ async def execute_after_hooks(
118
+ cls,
119
+ tool: Any,
120
+ payload: Any,
121
+ payload_extension: Optional[Dict[str, Any]] = None,
122
+ tool_call_id: Optional[str] = None,
123
+ agent_version: Optional[str] = None,
124
+ result: Any = None
125
+ ) -> None:
126
+ """
127
+ Execute all registered after-invocation hooks.
128
+
129
+ Args:
130
+ tool: The Tool object that was invoked.
131
+ payload: The payload sent to the tool.
132
+ payload_extension: Additional payload data.
133
+ tool_call_id: Unique ID of the tool call.
134
+ agent_version: Version of the agent that made the call.
135
+ result: The result returned by the tool invocation.
136
+ """
137
+ from loguru import logger
138
+
139
+ for hook in cls._after_hooks:
140
+ try:
141
+ if asyncio.iscoroutinefunction(hook):
142
+ await hook(tool, payload, payload_extension, tool_call_id, agent_version, result)
143
+ else:
144
+ hook(tool, payload, payload_extension, tool_call_id, agent_version, result)
145
+ except Exception as e:
146
+ logger.error(f"After-hook {hook.__name__} failed: {e}")
147
+
148
+ @classmethod
149
+ async def execute_error_hooks(
150
+ cls,
151
+ tool: Any,
152
+ payload: Any,
153
+ payload_extension: Optional[Dict[str, Any]] = None,
154
+ tool_call_id: Optional[str] = None,
155
+ agent_version: Optional[str] = None,
156
+ error: Optional[Exception] = None
157
+ ) -> None:
158
+ """
159
+ Execute all registered error hooks.
160
+
161
+ Args:
162
+ tool: The Tool object that failed.
163
+ payload: The payload sent to the tool.
164
+ payload_extension: Additional payload data.
165
+ tool_call_id: Unique ID of the tool call.
166
+ agent_version: Version of the agent that made the call.
167
+ error: The exception that occurred during invocation.
168
+ """
169
+ from loguru import logger
170
+
171
+ for hook in cls._error_hooks:
172
+ try:
173
+ if asyncio.iscoroutinefunction(hook):
174
+ await hook(tool, payload, payload_extension, tool_call_id, agent_version, error)
175
+ else:
176
+ hook(tool, payload, payload_extension, tool_call_id, agent_version, error)
177
+ except Exception as e:
178
+ logger.error(f"Error-hook {hook.__name__} failed: {e}")
179
+
180
+
181
+ def on_tool_before(
182
+ _func: Optional[Callable] = None,
183
+ *,
184
+ configuration: Optional[Configuration] = None
185
+ ):
186
+ """
187
+ Decorator to register a handler as a before-invocation hook for tool calls.
188
+
189
+ The decorated function will be executed before any tool invocation. The function:
190
+ - Must accept parameters: tool, payload, payload_extension, tool_call_id, agent_version
191
+ - Can be either synchronous or asynchronous
192
+ - Can perform validation, logging, or modification of invocation context
193
+
194
+ Args:
195
+ _func (Optional[Callable]):
196
+ The function to decorate (for direct usage like `@on_tool_before`).
197
+ configuration (Optional[Configuration]):
198
+ An optional configuration object (reserved for future use).
199
+
200
+ Example:
201
+ >>> from typing import Optional, Dict, Any
202
+ >>> from xpander_sdk import Tool
203
+ >>>
204
+ >>> @on_tool_before
205
+ ... async def validate_tool_input(
206
+ ... tool: Tool,
207
+ ... payload: Any,
208
+ ... payload_extension: Optional[Dict[str, Any]] = None,
209
+ ... tool_call_id: Optional[str] = None,
210
+ ... agent_version: Optional[str] = None
211
+ ... ):
212
+ ... logger.info(f"Pre-invoking tool: {tool.name}")
213
+ ... if not payload:
214
+ ... logger.warning("Empty payload provided")
215
+
216
+ >>> @on_tool_before
217
+ ... def log_tool_metrics(
218
+ ... tool: Tool,
219
+ ... payload: Any,
220
+ ... payload_extension: Optional[Dict[str, Any]] = None,
221
+ ... tool_call_id: Optional[str] = None,
222
+ ... agent_version: Optional[str] = None
223
+ ... ):
224
+ ... metrics.record_tool_invocation(tool.name, tool_call_id)
225
+ """
226
+
227
+ def decorator(func: Callable) -> Callable:
228
+ @wraps(func)
229
+ async def async_wrapper(*args, **kwargs):
230
+ return await func(*args, **kwargs)
231
+
232
+ @wraps(func)
233
+ def sync_wrapper(*args, **kwargs):
234
+ return func(*args, **kwargs)
235
+
236
+ wrapped = async_wrapper if iscoroutinefunction(func) else sync_wrapper
237
+
238
+ # Register hook in the registry
239
+ ToolHooksRegistry.register_before_hook(wrapped)
240
+
241
+ return wrapped
242
+
243
+ if _func and callable(_func):
244
+ return decorator(_func)
245
+
246
+ return decorator
247
+
248
+
249
+ def on_tool_after(
250
+ _func: Optional[Callable] = None,
251
+ *,
252
+ configuration: Optional[Configuration] = None
253
+ ):
254
+ """
255
+ Decorator to register a handler as an after-invocation hook for tool calls.
256
+
257
+ The decorated function will be executed after successful tool invocation. The function:
258
+ - Must accept parameters: tool, payload, payload_extension, tool_call_id, agent_version, result
259
+ - Can be either synchronous or asynchronous
260
+ - Can perform logging, analytics, or result processing
261
+
262
+ Args:
263
+ _func (Optional[Callable]):
264
+ The function to decorate (for direct usage like `@on_tool_after`).
265
+ configuration (Optional[Configuration]):
266
+ An optional configuration object (reserved for future use).
267
+
268
+ Example:
269
+ >>> from typing import Optional, Dict, Any
270
+ >>> from xpander_sdk import Tool
271
+ >>>
272
+ >>> @on_tool_after
273
+ ... async def log_tool_success(
274
+ ... tool: Tool,
275
+ ... payload: Any,
276
+ ... payload_extension: Optional[Dict[str, Any]] = None,
277
+ ... tool_call_id: Optional[str] = None,
278
+ ... agent_version: Optional[str] = None,
279
+ ... result: Any = None
280
+ ... ):
281
+ ... logger.info(f"Tool {tool.name} succeeded with result: {result}")
282
+ ... analytics.record_success(tool.name, tool_call_id)
283
+
284
+ >>> @on_tool_after
285
+ ... def cache_tool_result(
286
+ ... tool: Tool,
287
+ ... payload: Any,
288
+ ... payload_extension: Optional[Dict[str, Any]] = None,
289
+ ... tool_call_id: Optional[str] = None,
290
+ ... agent_version: Optional[str] = None,
291
+ ... result: Any = None
292
+ ... ):
293
+ ... cache.set(f"tool_{tool.id}_{hash(payload)}", result)
294
+ """
295
+
296
+ def decorator(func: Callable) -> Callable:
297
+ @wraps(func)
298
+ async def async_wrapper(*args, **kwargs):
299
+ return await func(*args, **kwargs)
300
+
301
+ @wraps(func)
302
+ def sync_wrapper(*args, **kwargs):
303
+ return func(*args, **kwargs)
304
+
305
+ wrapped = async_wrapper if iscoroutinefunction(func) else sync_wrapper
306
+
307
+ # Register hook in the registry
308
+ ToolHooksRegistry.register_after_hook(wrapped)
309
+
310
+ return wrapped
311
+
312
+ if _func and callable(_func):
313
+ return decorator(_func)
314
+
315
+ return decorator
316
+
317
+
318
+ def on_tool_error(
319
+ _func: Optional[Callable] = None,
320
+ *,
321
+ configuration: Optional[Configuration] = None
322
+ ):
323
+ """
324
+ Decorator to register a handler as an error hook for tool calls.
325
+
326
+ The decorated function will be executed when tool invocation fails. The function:
327
+ - Must accept parameters: tool, payload, payload_extension, tool_call_id, agent_version, error
328
+ - Can be either synchronous or asynchronous
329
+ - Can perform error logging, alerting, or recovery actions
330
+
331
+ Args:
332
+ _func (Optional[Callable]):
333
+ The function to decorate (for direct usage like `@on_tool_error`).
334
+ configuration (Optional[Configuration]):
335
+ An optional configuration object (reserved for future use).
336
+
337
+ Example:
338
+ >>> from typing import Optional, Dict, Any
339
+ >>> from xpander_sdk import Tool
340
+ >>>
341
+ >>> @on_tool_error
342
+ ... async def alert_on_tool_failure(
343
+ ... tool: Tool,
344
+ ... payload: Any,
345
+ ... payload_extension: Optional[Dict[str, Any]] = None,
346
+ ... tool_call_id: Optional[str] = None,
347
+ ... agent_version: Optional[str] = None,
348
+ ... error: Optional[Exception] = None
349
+ ... ):
350
+ ... logger.error(f"Tool {tool.name} failed: {error}")
351
+ ... await send_alert(f"Tool failure: {tool.name}", str(error))
352
+
353
+ >>> @on_tool_error
354
+ ... def log_tool_failure(
355
+ ... tool: Tool,
356
+ ... payload: Any,
357
+ ... payload_extension: Optional[Dict[str, Any]] = None,
358
+ ... tool_call_id: Optional[str] = None,
359
+ ... agent_version: Optional[str] = None,
360
+ ... error: Optional[Exception] = None
361
+ ... ):
362
+ ... error_log.write(f"{tool.name},{tool_call_id},{str(error)}\n")
363
+ """
364
+
365
+ def decorator(func: Callable) -> Callable:
366
+ @wraps(func)
367
+ async def async_wrapper(*args, **kwargs):
368
+ return await func(*args, **kwargs)
369
+
370
+ @wraps(func)
371
+ def sync_wrapper(*args, **kwargs):
372
+ return func(*args, **kwargs)
373
+
374
+ wrapped = async_wrapper if iscoroutinefunction(func) else sync_wrapper
375
+
376
+ # Register hook in the registry
377
+ ToolHooksRegistry.register_error_hook(wrapped)
378
+
379
+ return wrapped
380
+
381
+ if _func and callable(_func):
382
+ return decorator(_func)
383
+
384
+ return decorator
@@ -41,6 +41,7 @@ from xpander_sdk.modules.tools_repository.utils.schemas import (
41
41
  schema_enforcement_block_and_descriptions,
42
42
  )
43
43
  from xpander_sdk.utils.event_loop import run_sync
44
+ from xpander_sdk.modules.events.decorators.on_tool import ToolHooksRegistry
44
45
 
45
46
 
46
47
  class Tool(XPanderSharedModel):
@@ -376,6 +377,15 @@ class Tool(XPanderSharedModel):
376
377
  task_id=task_id,
377
378
  )
378
379
 
380
+ # Execute before hooks
381
+ await ToolHooksRegistry.execute_before_hooks(
382
+ tool=self,
383
+ payload=payload,
384
+ payload_extension=payload_extension,
385
+ tool_call_id=tool_call_id,
386
+ agent_version=agent_version
387
+ )
388
+
379
389
  try:
380
390
  if self.schema and payload:
381
391
  try:
@@ -403,17 +413,26 @@ class Tool(XPanderSharedModel):
403
413
 
404
414
  tool_invocation_result.result = result
405
415
  tool_invocation_result.is_success = True
406
- return tool_invocation_result
407
-
408
- tool_invocation_result.result = await self.acall_remote_tool(
409
- agent_id=agent_id,
410
- agent_version=agent_version,
416
+ else:
417
+ tool_invocation_result.result = await self.acall_remote_tool(
418
+ agent_id=agent_id,
419
+ agent_version=agent_version,
420
+ payload=payload,
421
+ payload_extension=payload_extension,
422
+ configuration=configuration,
423
+ task_id=task_id,
424
+ )
425
+ tool_invocation_result.is_success = True
426
+
427
+ # Execute after hooks on success
428
+ await ToolHooksRegistry.execute_after_hooks(
429
+ tool=self,
411
430
  payload=payload,
412
431
  payload_extension=payload_extension,
413
- configuration=configuration,
414
- task_id=task_id,
432
+ tool_call_id=tool_call_id,
433
+ agent_version=agent_version,
434
+ result=tool_invocation_result.result
415
435
  )
416
- tool_invocation_result.is_success = True
417
436
 
418
437
  except Exception as e:
419
438
  tool_invocation_result.is_error = True
@@ -423,6 +442,16 @@ class Tool(XPanderSharedModel):
423
442
  else:
424
443
  tool_invocation_result.status_code = 500
425
444
  tool_invocation_result.result = str(e)
445
+
446
+ # Execute error hooks on failure
447
+ await ToolHooksRegistry.execute_error_hooks(
448
+ tool=self,
449
+ payload=payload,
450
+ payload_extension=payload_extension,
451
+ tool_call_id=tool_call_id,
452
+ agent_version=agent_version,
453
+ error=e
454
+ )
426
455
 
427
456
  return tool_invocation_result
428
457
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xpander-sdk
3
- Version: 2.0.179
3
+ Version: 2.0.180
4
4
  Summary: xpander.ai Backend-as-a-service for AI Agents - SDK
5
5
  Home-page: https://www.xpander.ai
6
6
  Author: xpanderAI
@@ -1,4 +1,4 @@
1
- xpander_sdk/__init__.py,sha256=ETLxETELJT1j5Q4xL7sWC-Kz4FRps8IvLoYexNCDqcs,2760
1
+ xpander_sdk/__init__.py,sha256=34l3YcvIdkj81DTfMp_bgZgXpj2U1lTKpHQ0shwnc_8,2927
2
2
  xpander_sdk/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  xpander_sdk/consts/api_routes.py,sha256=dKUruLanvprTxZO15fR4OWe6ck8JQ8y64XmUJxsbeJY,2278
4
4
  xpander_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -39,10 +39,11 @@ xpander_sdk/modules/backend/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
39
39
  xpander_sdk/modules/backend/utils/mcp_oauth.py,sha256=K80bLjFW66TXm-wz5mbtZEeKjpeYr6ezhMFb5r3bhXA,5468
40
40
  xpander_sdk/modules/events/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  xpander_sdk/modules/events/events_module.py,sha256=DVlho7JxT6Jy8GeyuSakswmYwR18xqO2JcCJ-8Zc3s8,25317
42
- xpander_sdk/modules/events/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ xpander_sdk/modules/events/decorators/__init__.py,sha256=8GhR9afoLiS83a0OMLpn7TN2W6iTnlcVqos6bM34nac,129
43
43
  xpander_sdk/modules/events/decorators/on_boot.py,sha256=VGtoQcgs3g5bmx3Ze4QB_-ZwBESATYYVR0oZe35eCww,3076
44
44
  xpander_sdk/modules/events/decorators/on_shutdown.py,sha256=rFgChspnLDnZm9FS1K636dvZSQDkeugf2e3M83SDgAY,3127
45
45
  xpander_sdk/modules/events/decorators/on_task.py,sha256=G3jk0xzi3pqH96Bbut_GMJKExIlyyMYk4PbKfc6koa4,8551
46
+ xpander_sdk/modules/events/decorators/on_tool.py,sha256=ZacZ6tADjvl79ISqKxTSH1P0nZUS8C3mRwOL2SyLeZE,13750
46
47
  xpander_sdk/modules/events/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
48
  xpander_sdk/modules/events/models/deployments.py,sha256=6uwxFsybrZ-eHeohJzkm2RtQq4Eo_0xjHk7QouvszxU,1335
48
49
  xpander_sdk/modules/events/models/events.py,sha256=T_89pq48e7fMIbJcCbtM9Ocb6YKXQP7pbF6VbECiGcI,1550
@@ -74,7 +75,7 @@ xpander_sdk/modules/tools_repository/models/__init__.py,sha256=47DEQpj8HBSa-_TIm
74
75
  xpander_sdk/modules/tools_repository/models/mcp.py,sha256=qGpaiXKiuXw6gAcK8CW6ek6FkZNbBxDXUf1PWF6Tenw,1863
75
76
  xpander_sdk/modules/tools_repository/models/tool_invocation_result.py,sha256=Dhowt_fv8v8xWv7xMRJxo6hA8DawXKbWIrsJFMpt5H4,447
76
77
  xpander_sdk/modules/tools_repository/sub_modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- xpander_sdk/modules/tools_repository/sub_modules/tool.py,sha256=ylSVfekQQd0vPCKXPWN67tlkyVT7R4WsSQtoEZcBETw,22521
78
+ xpander_sdk/modules/tools_repository/sub_modules/tool.py,sha256=rivnznxi6CrrOWE1rukkBRmad2H-rthhrelC7ei1IXM,23617
78
79
  xpander_sdk/modules/tools_repository/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
80
  xpander_sdk/modules/tools_repository/utils/generic.py,sha256=m9FRaVGzRUj23tB52rP9K4O-nTsMSt9iwMxMcYsqfiY,1770
80
81
  xpander_sdk/modules/tools_repository/utils/local_tools.py,sha256=zp5P8hVnRUJQb-w-2jCEMV5eUB_awmvYfY_rin5qvEw,1875
@@ -86,8 +87,8 @@ xpander_sdk/utils/generic.py,sha256=XrRj2-L8c0YWpfPdDyXE-pVL-6lKF9VpyZzKHQ4wuCc,
86
87
  xpander_sdk/utils/tools.py,sha256=lyFkq2yP7DxBkyXYVlnFRwDhQCvf0fZZMDm5fBycze4,1244
87
88
  xpander_sdk/utils/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
89
  xpander_sdk/utils/agents/compactization_agent.py,sha256=pqaf7LVSyPFaeXU62dMPY6iQ160TFga1KJ0Kgu1dIgg,14449
89
- xpander_sdk-2.0.179.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
90
- xpander_sdk-2.0.179.dist-info/METADATA,sha256=6dcIt4ajwKjEFSmOUqg8MgzDwe7hXeEnckKXKAnxohc,17880
91
- xpander_sdk-2.0.179.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
92
- xpander_sdk-2.0.179.dist-info/top_level.txt,sha256=UCjnxQpsMy5Zoe7lmRuVDO6DI2V_6PgRFfm4oizRbVs,12
93
- xpander_sdk-2.0.179.dist-info/RECORD,,
90
+ xpander_sdk-2.0.180.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
91
+ xpander_sdk-2.0.180.dist-info/METADATA,sha256=NfaTIonGkunIZxzmX3C82qiGcKeewspcwnJhHcqIYOM,17880
92
+ xpander_sdk-2.0.180.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
93
+ xpander_sdk-2.0.180.dist-info/top_level.txt,sha256=UCjnxQpsMy5Zoe7lmRuVDO6DI2V_6PgRFfm4oizRbVs,12
94
+ xpander_sdk-2.0.180.dist-info/RECORD,,