iii-sdk 0.11.3.dev2__tar.gz → 0.11.4.dev2__tar.gz

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.
Files changed (58) hide show
  1. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/PKG-INFO +1 -1
  2. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/pyproject.toml +1 -1
  3. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/iii.py +0 -214
  4. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/types.py +0 -15
  5. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_api_triggers.py +36 -0
  6. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_async_api.py +0 -18
  7. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_bridge.py +10 -4
  8. iii_sdk-0.11.4.dev2/tests/test_queue_integration.py +358 -0
  9. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_rbac_workers.py +18 -6
  10. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_state.py +71 -0
  11. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_streams.py +114 -6
  12. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_sync_api.py +2 -3
  13. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/uv.lock +1 -1
  14. iii_sdk-0.11.3.dev2/tests/test_queue_integration.py +0 -136
  15. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/.gitignore +0 -0
  16. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/README.md +0 -0
  17. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/__init__.py +0 -0
  18. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/channels.py +0 -0
  19. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/errors.py +0 -0
  20. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/format_utils.py +0 -0
  21. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/iii_constants.py +0 -0
  22. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/iii_types.py +0 -0
  23. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/logger.py +0 -0
  24. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/otel_worker_gauges.py +0 -0
  25. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/state.py +0 -0
  26. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/stream.py +0 -0
  27. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/telemetry.py +0 -0
  28. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/telemetry_exporters.py +0 -0
  29. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/telemetry_types.py +0 -0
  30. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/triggers.py +0 -0
  31. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/utils.py +0 -0
  32. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/worker_metrics.py +0 -0
  33. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/conftest.py +0 -0
  34. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_channel_close_delay.py +0 -0
  35. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_context_propagation.py +0 -0
  36. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_data_channels.py +0 -0
  37. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_errors.py +0 -0
  38. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_format_utils.py +0 -0
  39. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_healthcheck.py +0 -0
  40. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_hold_process.py +0 -0
  41. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_http_external_functions_integration.py +0 -0
  42. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_iii_registration_dedup.py +0 -0
  43. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_init_api.py +0 -0
  44. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_invocation_exception.py +0 -0
  45. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_logger_function_ids.py +0 -0
  46. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_logger_otel.py +0 -0
  47. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_middleware.py +0 -0
  48. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_pubsub.py +0 -0
  49. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_register_function_args.py +0 -0
  50. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_streams_runtime_annotations.py +0 -0
  51. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_telemetry.py +0 -0
  52. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_telemetry_exporters.py +0 -0
  53. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_telemetry_types.py +0 -0
  54. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_trace_helpers.py +0 -0
  55. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_trigger_metadata.py +0 -0
  56. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_utils.py +0 -0
  57. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_worker_metadata.py +0 -0
  58. {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_worker_metrics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iii-sdk
3
- Version: 0.11.3.dev2
3
+ Version: 0.11.4.dev2
4
4
  Summary: III SDK for Python
5
5
  Project-URL: Homepage, https://github.com/iii-hq/sdk
6
6
  Project-URL: Repository, https://github.com/iii-hq/sdk
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "iii-sdk"
7
- version = "0.11.3.dev2"
7
+ version = "0.11.4.dev2"
8
8
  description = "III SDK for Python"
9
9
  authors = [{ name = "III" }]
10
10
  license = { text = "Apache-2.0" }
@@ -27,7 +27,6 @@ from .iii_constants import (
27
27
  InitOptions,
28
28
  )
29
29
  from .iii_types import (
30
- FunctionInfo,
31
30
  HttpInvocationConfig,
32
31
  InvocationResultMessage,
33
32
  InvokeFunctionMessage,
@@ -44,13 +43,10 @@ from .iii_types import (
44
43
  StreamChannelRef,
45
44
  TriggerActionEnqueue,
46
45
  TriggerActionVoid,
47
- TriggerInfo,
48
46
  TriggerRequest,
49
- TriggerTypeInfo,
50
47
  UnregisterFunctionMessage,
51
48
  UnregisterTriggerMessage,
52
49
  UnregisterTriggerTypeMessage,
53
- WorkerInfo,
54
50
  )
55
51
  from .stream import (
56
52
  IStream,
@@ -128,11 +124,6 @@ class III:
128
124
  self._reconnect_task: asyncio.Task[None] | None = None
129
125
  self._running = False
130
126
  self._receiver_task: asyncio.Task[None] | None = None
131
- self._functions_available_callbacks: set[
132
- Callable[[list[FunctionInfo]], None]
133
- ] = set()
134
- self._functions_available_trigger: Trigger | None = None
135
- self._functions_available_function_id: str | None = None
136
127
  self._reconnection_config = (
137
128
  self._options.reconnection_config or DEFAULT_RECONNECTION_CONFIG
138
129
  )
@@ -1096,148 +1087,6 @@ class III:
1096
1087
  invocation_id=invocation_id,
1097
1088
  )
1098
1089
 
1099
- def list_functions(self) -> list[FunctionInfo]:
1100
- """List all functions registered with the engine across all workers.
1101
-
1102
- Returns:
1103
- A list of ``FunctionInfo`` objects describing each function.
1104
-
1105
- Examples:
1106
- >>> for fn in iii.list_functions():
1107
- ... print(fn.function_id, fn.description)
1108
- """
1109
- return self._run_on_loop(self.list_functions_async())
1110
-
1111
- async def list_functions_async(self) -> list[FunctionInfo]:
1112
- """List all functions registered with the engine across all workers.
1113
-
1114
- Returns:
1115
- A list of ``FunctionInfo`` objects describing each function.
1116
-
1117
- Examples:
1118
- >>> for fn in await iii.list_functions_async():
1119
- ... print(fn.function_id, fn.description)
1120
- """
1121
- result = await self.trigger_async(
1122
- {"function_id": "engine::functions::list", "payload": {}}
1123
- )
1124
- functions_data = result.get("functions", [])
1125
- return [FunctionInfo(**f) for f in functions_data]
1126
-
1127
- def list_workers(self) -> list[WorkerInfo]:
1128
- """List all workers currently connected to the engine.
1129
-
1130
- Returns:
1131
- A list of ``WorkerInfo`` objects with worker metadata.
1132
-
1133
- Examples:
1134
- >>> for w in iii.list_workers():
1135
- ... print(w.name, w.worker_id)
1136
- """
1137
- return self._run_on_loop(self.list_workers_async())
1138
-
1139
- async def list_workers_async(self) -> list[WorkerInfo]:
1140
- """List all workers currently connected to the engine.
1141
-
1142
- Returns:
1143
- A list of ``WorkerInfo`` objects with worker metadata.
1144
-
1145
- Examples:
1146
- >>> for w in await iii.list_workers_async():
1147
- ... print(w.name, w.worker_id)
1148
- """
1149
- result = await self.trigger_async(
1150
- {"function_id": "engine::workers::list", "payload": {}}
1151
- )
1152
- workers_data = result.get("workers", [])
1153
- return [WorkerInfo(**w) for w in workers_data]
1154
-
1155
- def list_triggers(self, include_internal: bool = False) -> list[TriggerInfo]:
1156
- """List all triggers registered with the engine.
1157
-
1158
- Args:
1159
- include_internal: If ``True``, include engine-internal triggers
1160
- (e.g. ``functions-available``). Defaults to ``False``.
1161
-
1162
- Returns:
1163
- A list of ``TriggerInfo`` objects.
1164
-
1165
- Examples:
1166
- >>> triggers = iii.list_triggers()
1167
- >>> internal = iii.list_triggers(include_internal=True)
1168
- """
1169
- return self._run_on_loop(self.list_triggers_async(include_internal))
1170
-
1171
- async def list_triggers_async(
1172
- self, include_internal: bool = False
1173
- ) -> list[TriggerInfo]:
1174
- """List all triggers registered with the engine.
1175
-
1176
- Args:
1177
- include_internal: If ``True``, include engine-internal triggers
1178
- (e.g. ``functions-available``). Defaults to ``False``.
1179
-
1180
- Returns:
1181
- A list of ``TriggerInfo`` objects.
1182
-
1183
- Examples:
1184
- >>> triggers = await iii.list_triggers_async()
1185
- >>> internal = await iii.list_triggers_async(include_internal=True)
1186
- """
1187
- result = await self.trigger_async(
1188
- {
1189
- "function_id": "engine::triggers::list",
1190
- "payload": {"include_internal": include_internal},
1191
- }
1192
- )
1193
- triggers_data = result.get("triggers", [])
1194
- return [TriggerInfo(**t) for t in triggers_data]
1195
-
1196
- def list_trigger_types(
1197
- self, include_internal: bool = False
1198
- ) -> list[TriggerTypeInfo]:
1199
- """List all trigger types registered with the engine.
1200
-
1201
- Args:
1202
- include_internal: If ``True``, include engine-internal trigger
1203
- types (e.g. ``engine::functions-available``). Defaults to ``False``.
1204
-
1205
- Returns:
1206
- A list of ``TriggerTypeInfo`` objects with ``trigger_request_format``
1207
- and ``call_request_format`` schemas.
1208
-
1209
- Examples:
1210
- >>> trigger_types = iii.list_trigger_types()
1211
- >>> for tt in trigger_types:
1212
- ... print(tt.id, tt.trigger_request_format)
1213
- """
1214
- return self._run_on_loop(self.list_trigger_types_async(include_internal))
1215
-
1216
- async def list_trigger_types_async(
1217
- self, include_internal: bool = False
1218
- ) -> list[TriggerTypeInfo]:
1219
- """List all trigger types registered with the engine.
1220
-
1221
- Args:
1222
- include_internal: If ``True``, include engine-internal trigger
1223
- types (e.g. ``engine::functions-available``). Defaults to ``False``.
1224
-
1225
- Returns:
1226
- A list of ``TriggerTypeInfo`` objects with ``trigger_request_format``
1227
- and ``call_request_format`` schemas.
1228
-
1229
- Examples:
1230
- >>> trigger_types = await iii.list_trigger_types_async()
1231
- """
1232
- result = await self.trigger_async(
1233
- {
1234
- "function_id": "engine::trigger-types::list",
1235
- "payload": {"include_internal": include_internal},
1236
- }
1237
- )
1238
- types_data = result.get("trigger_types", [])
1239
- return [TriggerTypeInfo(**t) for t in types_data]
1240
-
1241
1090
  def create_channel(self, buffer_size: int | None = None) -> Channel:
1242
1091
  """Create a streaming channel pair for worker-to-worker data transfer.
1243
1092
 
@@ -1341,69 +1190,6 @@ class III:
1341
1190
  )
1342
1191
  asyncio.run_coroutine_threadsafe(self._send(msg), self._loop)
1343
1192
 
1344
- def on_functions_available(
1345
- self, callback: Callable[[list[FunctionInfo]], None]
1346
- ) -> Callable[[], None]:
1347
- """Subscribe to function-availability events from the engine.
1348
-
1349
- The callback fires whenever the set of available functions changes
1350
- (e.g. a new worker connects or a function is unregistered).
1351
-
1352
- Args:
1353
- callback (Callable[[list[FunctionInfo]], None]): Receives the
1354
- current list of ``FunctionInfo`` objects each time
1355
- availability changes.
1356
-
1357
- Returns:
1358
- A callable that unsubscribes when called. Calling the
1359
- returned function removes the callback and, if no callbacks
1360
- remain, tears down the internal trigger.
1361
-
1362
- Examples:
1363
- >>> def on_change(functions):
1364
- ... print("Available:", [f.function_id for f in functions])
1365
- >>> unsub = iii.on_functions_available(on_change)
1366
- >>> # later ...
1367
- >>> unsub()
1368
- """
1369
- self._functions_available_callbacks.add(callback)
1370
-
1371
- if not self._functions_available_trigger:
1372
- if not self._functions_available_function_id:
1373
- self._functions_available_function_id = (
1374
- f"iii.on_functions_available.{uuid.uuid4()}"
1375
- )
1376
-
1377
- function_id = self._functions_available_function_id
1378
- if function_id not in self._functions:
1379
-
1380
- async def handler(data: dict[str, Any]) -> None:
1381
- functions_data = data.get("functions", [])
1382
- functions = [FunctionInfo(**f) for f in functions_data]
1383
- for cb in list(self._functions_available_callbacks):
1384
- cb(functions)
1385
-
1386
- self.register_function({"id": function_id}, handler)
1387
-
1388
- self._functions_available_trigger = self.register_trigger(
1389
- {
1390
- "type": "engine::functions-available",
1391
- "function_id": function_id,
1392
- "config": {},
1393
- }
1394
- )
1395
-
1396
- def unsubscribe() -> None:
1397
- self._functions_available_callbacks.discard(callback)
1398
- if (
1399
- len(self._functions_available_callbacks) == 0
1400
- and self._functions_available_trigger
1401
- ):
1402
- self._functions_available_trigger.unregister()
1403
- self._functions_available_trigger = None
1404
-
1405
- return unsubscribe
1406
-
1407
1193
  def create_stream(self, stream_name: str, stream: IStream[Any]) -> None:
1408
1194
  """Register a custom stream implementation, overriding the engine default.
1409
1195
 
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Generic, Protocol, T
10
10
  from pydantic import BaseModel, ConfigDict, Field
11
11
 
12
12
  from .iii_types import (
13
- FunctionInfo,
14
13
  HttpInvocationConfig,
15
14
  RegisterFunctionInput,
16
15
  RegisterFunctionMessage,
@@ -19,7 +18,6 @@ from .iii_types import (
19
18
  RegisterTriggerTypeInput,
20
19
  RegisterTriggerTypeMessage,
21
20
  StreamChannelRef,
22
- TriggerInfo,
23
21
  TriggerRequest,
24
22
  )
25
23
  from .stream import IStream
@@ -80,11 +78,6 @@ class RemoteServiceFunctionData(BaseModel):
80
78
  handler: RemoteFunctionHandler
81
79
 
82
80
 
83
- # Type aliases for registration inputs
84
- # Callback type for functions available event
85
- FunctionsAvailableCallback = Callable[[list[FunctionInfo]], None]
86
-
87
-
88
81
  class IIIClient(Protocol):
89
82
  """Protocol for III client implementations."""
90
83
 
@@ -108,18 +101,10 @@ class IIIClient(Protocol):
108
101
 
109
102
  def unregister_trigger_type(self, trigger_type: RegisterTriggerTypeInput | dict[str, Any]) -> None: ...
110
103
 
111
- def list_trigger_types(self, include_internal: bool = False) -> list[Any]: ...
112
-
113
104
  def create_channel(self, buffer_size: int | None = None) -> Channel: ...
114
105
 
115
106
  def create_stream(self, stream_name: str, stream: IStream[Any]) -> None: ...
116
107
 
117
- def list_functions(self) -> list[FunctionInfo]: ...
118
-
119
- def list_triggers(self, include_internal: bool = False) -> list[TriggerInfo]: ...
120
-
121
- def on_functions_available(self, callback: FunctionsAvailableCallback) -> Callable[[], None]: ...
122
-
123
108
  def shutdown(self) -> None: ...
124
109
 
125
110
 
@@ -194,6 +194,42 @@ async def test_custom_status_code(engine_http_url, iii_client: III):
194
194
  trigger.unregister()
195
195
 
196
196
 
197
+ @pytest.mark.asyncio
198
+ async def test_content_type_on_api_response_return(engine_http_url, iii_client: III):
199
+ """Returning an ApiResponse dict with headers should set the response Content-Type."""
200
+ xml_body = '<?xml version="1.0" encoding="UTF-8"?><note><to>user</to><body>hello</body></note>'
201
+
202
+ def handler(_input_data):
203
+ return {
204
+ "status_code": 200,
205
+ "headers": {"Content-Type": "text/xml"},
206
+ "body": xml_body,
207
+ }
208
+
209
+ fn_ref = iii_client.register_function({"id": "test.api.xml.return.py"}, handler)
210
+ trigger = iii_client.register_trigger(
211
+ {
212
+ "type": "http",
213
+ "function_id": "test.api.xml.return.py",
214
+ "config": {
215
+ "api_path": "test/py/xml-return",
216
+ "http_method": "POST",
217
+ },
218
+ }
219
+ )
220
+
221
+ time.sleep(0.3)
222
+
223
+ async with aiohttp.ClientSession() as session:
224
+ async with session.post(f"{engine_http_url}/test/py/xml-return") as resp:
225
+ assert resp.status == 200
226
+ assert resp.headers.get("content-type") == "text/xml"
227
+ assert await resp.text() == xml_body
228
+
229
+ fn_ref.unregister()
230
+ trigger.unregister()
231
+
232
+
197
233
  @pytest.mark.asyncio
198
234
  async def test_download_pdf_streaming(engine_http_url, iii_client: III):
199
235
  """Stream a PDF file as a download response."""
@@ -10,21 +10,6 @@ def test_trigger_async_is_coroutine_function():
10
10
  assert inspect.iscoroutinefunction(III.trigger_async)
11
11
 
12
12
 
13
- def test_list_functions_async_is_coroutine_function():
14
- assert hasattr(III, "list_functions_async")
15
- assert inspect.iscoroutinefunction(III.list_functions_async)
16
-
17
-
18
- def test_list_workers_async_is_coroutine_function():
19
- assert hasattr(III, "list_workers_async")
20
- assert inspect.iscoroutinefunction(III.list_workers_async)
21
-
22
-
23
- def test_list_triggers_async_is_coroutine_function():
24
- assert hasattr(III, "list_triggers_async")
25
- assert inspect.iscoroutinefunction(III.list_triggers_async)
26
-
27
-
28
13
  def test_create_channel_async_is_coroutine_function():
29
14
  assert hasattr(III, "create_channel_async")
30
15
  assert inspect.iscoroutinefunction(III.create_channel_async)
@@ -44,9 +29,6 @@ def test_async_methods_have_docstrings():
44
29
  """All public async methods must have docstrings."""
45
30
  async_methods = [
46
31
  "trigger_async",
47
- "list_functions_async",
48
- "list_workers_async",
49
- "list_triggers_async",
50
32
  "create_channel_async",
51
33
  "connect_async",
52
34
  "shutdown_async",
@@ -4,7 +4,7 @@ import asyncio
4
4
 
5
5
  import pytest
6
6
 
7
- from iii import TriggerAction
7
+ from iii import FunctionInfo, TriggerAction
8
8
  from iii.iii import III
9
9
 
10
10
 
@@ -21,7 +21,10 @@ async def wait_for(condition, timeout=5.0, interval=0.1):
21
21
  @pytest.mark.asyncio
22
22
  async def test_connect_successfully(iii_client: III):
23
23
  """SDK connects to the engine and can list functions."""
24
- functions = iii_client.list_functions()
24
+ result = iii_client.trigger(
25
+ {"function_id": "engine::functions::list", "payload": {}}
26
+ )
27
+ functions = [FunctionInfo(**f) for f in result.get("functions", [])]
25
28
  assert isinstance(functions, list)
26
29
 
27
30
 
@@ -83,13 +86,16 @@ async def test_invoke_function_fire_and_forget(iii_client: III):
83
86
 
84
87
  @pytest.mark.asyncio
85
88
  async def test_list_registered_functions(iii_client: III):
86
- """Registered function IDs appear in list_functions()."""
89
+ """Registered function IDs appear in the engine functions list."""
87
90
  fn1 = iii_client.register_function({"id": "test.bridge.py.list.func1"}, lambda _: {})
88
91
  fn2 = iii_client.register_function({"id": "test.bridge.py.list.func2"}, lambda _: {})
89
92
  await asyncio.sleep(0.3)
90
93
 
91
94
  try:
92
- functions = iii_client.list_functions()
95
+ result = iii_client.trigger(
96
+ {"function_id": "engine::functions::list", "payload": {}}
97
+ )
98
+ functions = [FunctionInfo(**f) for f in result.get("functions", [])]
93
99
  function_ids = [f.function_id for f in functions]
94
100
 
95
101
  assert "test.bridge.py.list.func1" in function_ids