indexify 0.2.48__py3-none-any.whl → 0.3.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.
Files changed (59) hide show
  1. indexify/{cli.py → cli/cli.py} +75 -82
  2. indexify/executor/README.md +35 -0
  3. indexify/executor/api_objects.py +9 -3
  4. indexify/executor/downloader.py +5 -5
  5. indexify/executor/executor.py +35 -22
  6. indexify/executor/function_executor/function_executor.py +14 -3
  7. indexify/executor/function_executor/function_executor_state.py +13 -10
  8. indexify/executor/function_executor/invocation_state_client.py +2 -1
  9. indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py +22 -10
  10. indexify/executor/function_executor/single_task_runner.py +43 -26
  11. indexify/executor/function_executor/task_input.py +1 -3
  12. indexify/executor/task_fetcher.py +5 -7
  13. indexify/executor/task_reporter.py +3 -5
  14. indexify/executor/task_runner.py +31 -24
  15. indexify/function_executor/README.md +18 -0
  16. indexify/function_executor/handlers/run_function/function_inputs_loader.py +13 -14
  17. indexify/function_executor/handlers/run_function/handler.py +16 -40
  18. indexify/function_executor/handlers/run_function/request_validator.py +7 -5
  19. indexify/function_executor/handlers/run_function/response_helper.py +6 -8
  20. indexify/function_executor/initialize_request_validator.py +1 -2
  21. indexify/function_executor/invocation_state/invocation_state_proxy_server.py +1 -1
  22. indexify/function_executor/invocation_state/proxied_invocation_state.py +1 -3
  23. indexify/function_executor/main.py +50 -0
  24. indexify/function_executor/proto/configuration.py +8 -0
  25. indexify/function_executor/proto/function_executor.proto +9 -4
  26. indexify/function_executor/proto/function_executor_pb2.py +24 -24
  27. indexify/function_executor/proto/function_executor_pb2.pyi +24 -4
  28. indexify/function_executor/server.py +4 -6
  29. indexify/function_executor/{function_executor_service.py → service.py} +35 -24
  30. indexify/utils/README.md +3 -0
  31. indexify/{common_util.py → utils/http_client.py} +2 -2
  32. indexify/{logging.py → utils/logging.py} +36 -2
  33. indexify-0.3.1.dist-info/METADATA +38 -0
  34. indexify-0.3.1.dist-info/RECORD +44 -0
  35. {indexify-0.2.48.dist-info → indexify-0.3.1.dist-info}/WHEEL +1 -1
  36. indexify-0.3.1.dist-info/entry_points.txt +4 -0
  37. indexify/__init__.py +0 -31
  38. indexify/data_loaders/__init__.py +0 -58
  39. indexify/data_loaders/local_directory_loader.py +0 -37
  40. indexify/data_loaders/url_loader.py +0 -52
  41. indexify/error.py +0 -8
  42. indexify/functions_sdk/data_objects.py +0 -27
  43. indexify/functions_sdk/graph.py +0 -364
  44. indexify/functions_sdk/graph_definition.py +0 -63
  45. indexify/functions_sdk/graph_validation.py +0 -70
  46. indexify/functions_sdk/image.py +0 -222
  47. indexify/functions_sdk/indexify_functions.py +0 -354
  48. indexify/functions_sdk/invocation_state/invocation_state.py +0 -22
  49. indexify/functions_sdk/invocation_state/local_invocation_state.py +0 -30
  50. indexify/functions_sdk/object_serializer.py +0 -68
  51. indexify/functions_sdk/pipeline.py +0 -33
  52. indexify/http_client.py +0 -379
  53. indexify/remote_graph.py +0 -138
  54. indexify/remote_pipeline.py +0 -25
  55. indexify/settings.py +0 -1
  56. indexify-0.2.48.dist-info/LICENSE.txt +0 -201
  57. indexify-0.2.48.dist-info/METADATA +0 -154
  58. indexify-0.2.48.dist-info/RECORD +0 -60
  59. indexify-0.2.48.dist-info/entry_points.txt +0 -3
@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
24
24
 
25
25
 
26
26
  DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
27
- b'\n8indexify/function_executor/proto/function_executor.proto\x12\x19\x66unction_executor_service"i\n\x10SerializedObject\x12\x0f\n\x05\x62ytes\x18\x01 \x01(\x0cH\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x19\n\x0c\x63ontent_type\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x06\n\x04\x64\x61taB\x0f\n\r_content_type"\x88\x02\n\x11InitializeRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x05 \x01(\tH\x03\x88\x01\x01\x12?\n\x05graph\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x04\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x08\n\x06_graph"6\n\x12InitializeResponse\x12\x14\n\x07success\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_success"\x80\x01\n\x19SetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\x1c\n\x1aSetInvocationStateResponse"5\n\x19GetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_key"\x81\x01\n\x1aGetInvocationStateResponse\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\xf7\x01\n\x16InvocationStateRequest\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07task_id\x18\x02 \x01(\tH\x02\x88\x01\x01\x12\x43\n\x03set\x18\x03 \x01(\x0b\x32\x34.function_executor_service.SetInvocationStateRequestH\x00\x12\x43\n\x03get\x18\x04 \x01(\x0b\x32\x34.function_executor_service.GetInvocationStateRequestH\x00\x42\t\n\x07requestB\r\n\x0b_request_idB\n\n\x08_task_id"\xfb\x01\n\x17InvocationStateResponse\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07success\x18\x02 \x01(\x08H\x02\x88\x01\x01\x12\x44\n\x03set\x18\x03 \x01(\x0b\x32\x35.function_executor_service.SetInvocationStateResponseH\x00\x12\x44\n\x03get\x18\x04 \x01(\x0b\x32\x35.function_executor_service.GetInvocationStateResponseH\x00\x42\n\n\x08responseB\r\n\x0b_request_idB\n\n\x08_success"N\n\x0e\x46unctionOutput\x12<\n\x07outputs\x18\x01 \x03(\x0b\x32+.function_executor_service.SerializedObject"\x1d\n\x0cRouterOutput\x12\r\n\x05\x65\x64ges\x18\x01 \x03(\t"\xb0\x02\n\x0eRunTaskRequest\x12 \n\x13graph_invocation_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07task_id\x18\x06 \x01(\tH\x01\x88\x01\x01\x12H\n\x0e\x66unction_input\x18\t \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x02\x88\x01\x01\x12M\n\x13\x66unction_init_value\x18\n \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x03\x88\x01\x01\x42\x16\n\x14_graph_invocation_idB\n\n\x08_task_idB\x11\n\x0f_function_inputB\x16\n\x14_function_init_value"\xf1\x02\n\x0fRunTaskResponse\x12\x14\n\x07task_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12G\n\x0f\x66unction_output\x18\x02 \x01(\x0b\x32).function_executor_service.FunctionOutputH\x01\x88\x01\x01\x12\x43\n\rrouter_output\x18\x03 \x01(\x0b\x32\'.function_executor_service.RouterOutputH\x02\x88\x01\x01\x12\x13\n\x06stdout\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06stderr\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x17\n\nis_reducer\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x14\n\x07success\x18\x07 \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_task_idB\x12\n\x10_function_outputB\x10\n\x0e_router_outputB\t\n\x07_stdoutB\t\n\x07_stderrB\r\n\x0b_is_reducerB\n\n\x08_success2\xf2\x02\n\x10\x46unctionExecutor\x12i\n\ninitialize\x12,.function_executor_service.InitializeRequest\x1a-.function_executor_service.InitializeResponse\x12\x8f\x01\n"initialize_invocation_state_server\x12\x32.function_executor_service.InvocationStateResponse\x1a\x31.function_executor_service.InvocationStateRequest(\x01\x30\x01\x12\x61\n\x08run_task\x12).function_executor_service.RunTaskRequest\x1a*.function_executor_service.RunTaskResponseb\x06proto3'
27
+ b'\n8indexify/function_executor/proto/function_executor.proto\x12\x19\x66unction_executor_service"i\n\x10SerializedObject\x12\x0f\n\x05\x62ytes\x18\x01 \x01(\x0cH\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x19\n\x0c\x63ontent_type\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x06\n\x04\x64\x61taB\x0f\n\r_content_type"\x88\x02\n\x11InitializeRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x05 \x01(\tH\x03\x88\x01\x01\x12?\n\x05graph\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x04\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x08\n\x06_graph"f\n\x12InitializeResponse\x12\x14\n\x07success\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1b\n\x0e\x63ustomer_error\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\n\n\x08_successB\x11\n\x0f_customer_error"\x80\x01\n\x19SetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\x1c\n\x1aSetInvocationStateResponse"5\n\x19GetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_key"\x81\x01\n\x1aGetInvocationStateResponse\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\xf7\x01\n\x16InvocationStateRequest\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07task_id\x18\x02 \x01(\tH\x02\x88\x01\x01\x12\x43\n\x03set\x18\x03 \x01(\x0b\x32\x34.function_executor_service.SetInvocationStateRequestH\x00\x12\x43\n\x03get\x18\x04 \x01(\x0b\x32\x34.function_executor_service.GetInvocationStateRequestH\x00\x42\t\n\x07requestB\r\n\x0b_request_idB\n\n\x08_task_id"\xfb\x01\n\x17InvocationStateResponse\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07success\x18\x02 \x01(\x08H\x02\x88\x01\x01\x12\x44\n\x03set\x18\x03 \x01(\x0b\x32\x35.function_executor_service.SetInvocationStateResponseH\x00\x12\x44\n\x03get\x18\x04 \x01(\x0b\x32\x35.function_executor_service.GetInvocationStateResponseH\x00\x42\n\n\x08responseB\r\n\x0b_request_idB\n\n\x08_success"N\n\x0e\x46unctionOutput\x12<\n\x07outputs\x18\x01 \x03(\x0b\x32+.function_executor_service.SerializedObject"\x1d\n\x0cRouterOutput\x12\r\n\x05\x65\x64ges\x18\x01 \x03(\t"\xda\x03\n\x0eRunTaskRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x04 \x01(\tH\x03\x88\x01\x01\x12 \n\x13graph_invocation_id\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07task_id\x18\x06 \x01(\tH\x05\x88\x01\x01\x12H\n\x0e\x66unction_input\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x06\x88\x01\x01\x12M\n\x13\x66unction_init_value\x18\x08 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x07\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x16\n\x14_graph_invocation_idB\n\n\x08_task_idB\x11\n\x0f_function_inputB\x16\n\x14_function_init_value"\xf1\x02\n\x0fRunTaskResponse\x12\x14\n\x07task_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12G\n\x0f\x66unction_output\x18\x02 \x01(\x0b\x32).function_executor_service.FunctionOutputH\x01\x88\x01\x01\x12\x43\n\rrouter_output\x18\x03 \x01(\x0b\x32\'.function_executor_service.RouterOutputH\x02\x88\x01\x01\x12\x13\n\x06stdout\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06stderr\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x17\n\nis_reducer\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x14\n\x07success\x18\x07 \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_task_idB\x12\n\x10_function_outputB\x10\n\x0e_router_outputB\t\n\x07_stdoutB\t\n\x07_stderrB\r\n\x0b_is_reducerB\n\n\x08_success2\xf2\x02\n\x10\x46unctionExecutor\x12i\n\ninitialize\x12,.function_executor_service.InitializeRequest\x1a-.function_executor_service.InitializeResponse\x12\x8f\x01\n"initialize_invocation_state_server\x12\x32.function_executor_service.InvocationStateResponse\x1a\x31.function_executor_service.InvocationStateRequest(\x01\x30\x01\x12\x61\n\x08run_task\x12).function_executor_service.RunTaskRequest\x1a*.function_executor_service.RunTaskResponseb\x06proto3'
28
28
  )
29
29
 
30
30
  _globals = globals()
@@ -39,27 +39,27 @@ if not _descriptor._USE_C_DESCRIPTORS:
39
39
  _globals["_INITIALIZEREQUEST"]._serialized_start = 195
40
40
  _globals["_INITIALIZEREQUEST"]._serialized_end = 459
41
41
  _globals["_INITIALIZERESPONSE"]._serialized_start = 461
42
- _globals["_INITIALIZERESPONSE"]._serialized_end = 515
43
- _globals["_SETINVOCATIONSTATEREQUEST"]._serialized_start = 518
44
- _globals["_SETINVOCATIONSTATEREQUEST"]._serialized_end = 646
45
- _globals["_SETINVOCATIONSTATERESPONSE"]._serialized_start = 648
46
- _globals["_SETINVOCATIONSTATERESPONSE"]._serialized_end = 676
47
- _globals["_GETINVOCATIONSTATEREQUEST"]._serialized_start = 678
48
- _globals["_GETINVOCATIONSTATEREQUEST"]._serialized_end = 731
49
- _globals["_GETINVOCATIONSTATERESPONSE"]._serialized_start = 734
50
- _globals["_GETINVOCATIONSTATERESPONSE"]._serialized_end = 863
51
- _globals["_INVOCATIONSTATEREQUEST"]._serialized_start = 866
52
- _globals["_INVOCATIONSTATEREQUEST"]._serialized_end = 1113
53
- _globals["_INVOCATIONSTATERESPONSE"]._serialized_start = 1116
54
- _globals["_INVOCATIONSTATERESPONSE"]._serialized_end = 1367
55
- _globals["_FUNCTIONOUTPUT"]._serialized_start = 1369
56
- _globals["_FUNCTIONOUTPUT"]._serialized_end = 1447
57
- _globals["_ROUTEROUTPUT"]._serialized_start = 1449
58
- _globals["_ROUTEROUTPUT"]._serialized_end = 1478
59
- _globals["_RUNTASKREQUEST"]._serialized_start = 1481
60
- _globals["_RUNTASKREQUEST"]._serialized_end = 1785
61
- _globals["_RUNTASKRESPONSE"]._serialized_start = 1788
62
- _globals["_RUNTASKRESPONSE"]._serialized_end = 2157
63
- _globals["_FUNCTIONEXECUTOR"]._serialized_start = 2160
64
- _globals["_FUNCTIONEXECUTOR"]._serialized_end = 2530
42
+ _globals["_INITIALIZERESPONSE"]._serialized_end = 563
43
+ _globals["_SETINVOCATIONSTATEREQUEST"]._serialized_start = 566
44
+ _globals["_SETINVOCATIONSTATEREQUEST"]._serialized_end = 694
45
+ _globals["_SETINVOCATIONSTATERESPONSE"]._serialized_start = 696
46
+ _globals["_SETINVOCATIONSTATERESPONSE"]._serialized_end = 724
47
+ _globals["_GETINVOCATIONSTATEREQUEST"]._serialized_start = 726
48
+ _globals["_GETINVOCATIONSTATEREQUEST"]._serialized_end = 779
49
+ _globals["_GETINVOCATIONSTATERESPONSE"]._serialized_start = 782
50
+ _globals["_GETINVOCATIONSTATERESPONSE"]._serialized_end = 911
51
+ _globals["_INVOCATIONSTATEREQUEST"]._serialized_start = 914
52
+ _globals["_INVOCATIONSTATEREQUEST"]._serialized_end = 1161
53
+ _globals["_INVOCATIONSTATERESPONSE"]._serialized_start = 1164
54
+ _globals["_INVOCATIONSTATERESPONSE"]._serialized_end = 1415
55
+ _globals["_FUNCTIONOUTPUT"]._serialized_start = 1417
56
+ _globals["_FUNCTIONOUTPUT"]._serialized_end = 1495
57
+ _globals["_ROUTEROUTPUT"]._serialized_start = 1497
58
+ _globals["_ROUTEROUTPUT"]._serialized_end = 1526
59
+ _globals["_RUNTASKREQUEST"]._serialized_start = 1529
60
+ _globals["_RUNTASKREQUEST"]._serialized_end = 2003
61
+ _globals["_RUNTASKRESPONSE"]._serialized_start = 2006
62
+ _globals["_RUNTASKRESPONSE"]._serialized_end = 2375
63
+ _globals["_FUNCTIONEXECUTOR"]._serialized_start = 2378
64
+ _globals["_FUNCTIONEXECUTOR"]._serialized_end = 2748
65
65
  # @@protoc_insertion_point(module_scope)
@@ -34,23 +34,27 @@ class InitializeRequest(_message.Message):
34
34
  GRAPH_FIELD_NUMBER: _ClassVar[int]
35
35
  namespace: str
36
36
  graph_name: str
37
- graph_version: int
37
+ graph_version: str
38
38
  function_name: str
39
39
  graph: SerializedObject
40
40
  def __init__(
41
41
  self,
42
42
  namespace: _Optional[str] = ...,
43
43
  graph_name: _Optional[str] = ...,
44
- graph_version: _Optional[int] = ...,
44
+ graph_version: _Optional[str] = ...,
45
45
  function_name: _Optional[str] = ...,
46
46
  graph: _Optional[_Union[SerializedObject, _Mapping]] = ...,
47
47
  ) -> None: ...
48
48
 
49
49
  class InitializeResponse(_message.Message):
50
- __slots__ = ("success",)
50
+ __slots__ = ("success", "customer_error")
51
51
  SUCCESS_FIELD_NUMBER: _ClassVar[int]
52
+ CUSTOMER_ERROR_FIELD_NUMBER: _ClassVar[int]
52
53
  success: bool
53
- def __init__(self, success: bool = ...) -> None: ...
54
+ customer_error: str
55
+ def __init__(
56
+ self, success: bool = ..., customer_error: _Optional[str] = ...
57
+ ) -> None: ...
54
58
 
55
59
  class SetInvocationStateRequest(_message.Message):
56
60
  __slots__ = ("key", "value")
@@ -138,21 +142,37 @@ class RouterOutput(_message.Message):
138
142
 
139
143
  class RunTaskRequest(_message.Message):
140
144
  __slots__ = (
145
+ "namespace",
146
+ "graph_name",
147
+ "graph_version",
148
+ "function_name",
141
149
  "graph_invocation_id",
142
150
  "task_id",
143
151
  "function_input",
144
152
  "function_init_value",
145
153
  )
154
+ NAMESPACE_FIELD_NUMBER: _ClassVar[int]
155
+ GRAPH_NAME_FIELD_NUMBER: _ClassVar[int]
156
+ GRAPH_VERSION_FIELD_NUMBER: _ClassVar[int]
157
+ FUNCTION_NAME_FIELD_NUMBER: _ClassVar[int]
146
158
  GRAPH_INVOCATION_ID_FIELD_NUMBER: _ClassVar[int]
147
159
  TASK_ID_FIELD_NUMBER: _ClassVar[int]
148
160
  FUNCTION_INPUT_FIELD_NUMBER: _ClassVar[int]
149
161
  FUNCTION_INIT_VALUE_FIELD_NUMBER: _ClassVar[int]
162
+ namespace: str
163
+ graph_name: str
164
+ graph_version: str
165
+ function_name: str
150
166
  graph_invocation_id: str
151
167
  task_id: str
152
168
  function_input: SerializedObject
153
169
  function_init_value: SerializedObject
154
170
  def __init__(
155
171
  self,
172
+ namespace: _Optional[str] = ...,
173
+ graph_name: _Optional[str] = ...,
174
+ graph_version: _Optional[str] = ...,
175
+ function_name: _Optional[str] = ...,
156
176
  graph_invocation_id: _Optional[str] = ...,
157
177
  task_id: _Optional[str] = ...,
158
178
  function_input: _Optional[_Union[SerializedObject, _Mapping]] = ...,
@@ -2,11 +2,9 @@ from concurrent.futures import ThreadPoolExecutor
2
2
 
3
3
  import grpc
4
4
 
5
- from .function_executor_service import FunctionExecutorService
6
5
  from .proto.configuration import GRPC_SERVER_OPTIONS
7
- from .proto.function_executor_pb2_grpc import (
8
- add_FunctionExecutorServicer_to_server,
9
- )
6
+ from .proto.function_executor_pb2_grpc import add_FunctionExecutorServicer_to_server
7
+ from .service import Service
10
8
 
11
9
  # Temporary limit until we have a better way to control this.
12
10
  # This limits the number of concurrent tasks that Function Executor can run.
@@ -14,9 +12,9 @@ MAX_RPC_CONCURRENCY = 100
14
12
 
15
13
 
16
14
  class Server:
17
- def __init__(self, server_address: str, service: FunctionExecutorService):
15
+ def __init__(self, server_address: str, service: Service):
18
16
  self._server_address: str = server_address
19
- self._service: FunctionExecutorService = service
17
+ self._service: Service = service
20
18
 
21
19
  def run(self):
22
20
  """Runs Function Executor Service at the configured address."""
@@ -2,21 +2,15 @@ from typing import Iterator, Optional, Union
2
2
 
3
3
  import grpc
4
4
  import structlog
5
-
6
- from indexify.functions_sdk.indexify_functions import (
7
- IndexifyFunction,
8
- IndexifyRouter,
9
- )
10
- from indexify.functions_sdk.object_serializer import get_serializer
5
+ from tensorlake.functions_sdk.functions import TensorlakeCompute
6
+ from tensorlake.functions_sdk.object_serializer import get_serializer
11
7
 
12
8
  from .handlers.run_function.handler import Handler as RunTaskHandler
13
9
  from .handlers.run_function.request_validator import (
14
10
  RequestValidator as RunTaskRequestValidator,
15
11
  )
16
12
  from .initialize_request_validator import InitializeRequestValidator
17
- from .invocation_state.invocation_state_proxy_server import (
18
- InvocationStateProxyServer,
19
- )
13
+ from .invocation_state.invocation_state_proxy_server import InvocationStateProxyServer
20
14
  from .invocation_state.proxied_invocation_state import ProxiedInvocationState
21
15
  from .proto.function_executor_pb2 import (
22
16
  InitializeRequest,
@@ -28,14 +22,14 @@ from .proto.function_executor_pb2 import (
28
22
  from .proto.function_executor_pb2_grpc import FunctionExecutorServicer
29
23
 
30
24
 
31
- class FunctionExecutorService(FunctionExecutorServicer):
25
+ class Service(FunctionExecutorServicer):
32
26
  def __init__(self):
33
27
  self._logger = structlog.get_logger(module=__name__)
34
28
  self._namespace: Optional[str] = None
35
29
  self._graph_name: Optional[str] = None
36
- self._graph_version: Optional[int] = None
30
+ self._graph_version: Optional[str] = None
37
31
  self._function_name: Optional[str] = None
38
- self._function: Optional[Union[IndexifyFunction, IndexifyRouter]] = None
32
+ self._function: Optional[Union[TensorlakeCompute, TensorlakeCompute]] = None
39
33
  self._invocation_state_proxy_server: Optional[InvocationStateProxyServer] = None
40
34
 
41
35
  def initialize(
@@ -55,24 +49,22 @@ class FunctionExecutorService(FunctionExecutorServicer):
55
49
  # implementing smart caching in customer code. E.g. load a model into GPU only once and
56
50
  # share the model's file descriptor between all tasks or download function configuration
57
51
  # only once.
58
- graph_serializer = get_serializer(request.graph.content_type)
59
-
60
- try:
61
- graph = graph_serializer.deserialize(request.graph.bytes)
62
- except Exception as e:
63
- self._logger.error(f"Caught exception {e}")
64
- return InitializeResponse(success=False)
65
-
66
- self._function = graph_serializer.deserialize(graph[request.function_name])
67
-
68
52
  self._logger = self._logger.bind(
69
53
  namespace=request.namespace,
70
54
  graph_name=request.graph_name,
71
- graph_version=str(request.graph_version),
55
+ graph_version=request.graph_version,
72
56
  function_name=request.function_name,
73
57
  )
74
- self._logger.info("initialized function executor service")
58
+ graph_serializer = get_serializer(request.graph.content_type)
59
+ try:
60
+ # Process user controlled input in a try-except block to not treat errors here as our
61
+ # internal platform errors.
62
+ graph = graph_serializer.deserialize(request.graph.bytes)
63
+ self._function = graph_serializer.deserialize(graph[request.function_name])
64
+ except Exception as e:
65
+ return InitializeResponse(success=False, customer_error=str(e))
75
66
 
67
+ self._logger.info("initialized function executor service")
76
68
  return InitializeResponse(success=True)
77
69
 
78
70
  def initialize_invocation_state_server(
@@ -94,6 +86,25 @@ class FunctionExecutorService(FunctionExecutorServicer):
94
86
  # If our code raises an exception the grpc framework converts it into GRPC_STATUS_UNKNOWN
95
87
  # error with the exception message. Differentiating errors is not needed for now.
96
88
  RunTaskRequestValidator(request=request).check()
89
+
90
+ # Fail with internal error as this happened due to wrong task routing to this Server.
91
+ if request.namespace != self._namespace:
92
+ raise ValueError(
93
+ f"This Function Executor is not initialized for this namespace {request.namespace}"
94
+ )
95
+ if request.graph_name != self._graph_name:
96
+ raise ValueError(
97
+ f"This Function Executor is not initialized for this graph {request.graph_name}"
98
+ )
99
+ if request.graph_version != self._graph_version:
100
+ raise ValueError(
101
+ f"This Function Executor is not initialized for this graph version {request.graph_version}"
102
+ )
103
+ if request.function_name != self._function_name:
104
+ raise ValueError(
105
+ f"This Function Executor is not initialized for this function {request.function_name}"
106
+ )
107
+
97
108
  return RunTaskHandler(
98
109
  request=request,
99
110
  graph_name=self._graph_name,
@@ -0,0 +1,3 @@
1
+ ## Overview
2
+
3
+ Put here common functionality that is useful for code written in Python.
@@ -7,7 +7,7 @@ from httpx import AsyncClient, Client
7
7
 
8
8
  def get_httpx_client(
9
9
  config_path: Optional[str] = None, make_async: Optional[bool] = False
10
- ) -> AsyncClient | Client:
10
+ ) -> Union[AsyncClient, Client]:
11
11
  """
12
12
  Creates and returns an httpx.Client instance, optionally configured with TLS settings from a YAML config file.
13
13
 
@@ -54,7 +54,7 @@ def get_sync_or_async_client(
54
54
  cert_path: Optional[str] = None,
55
55
  key_path: Optional[str] = None,
56
56
  ca_bundle_path: Optional[str] = None,
57
- ) -> AsyncClient | Client:
57
+ ) -> Union[AsyncClient, Client]:
58
58
  """
59
59
  Creates and returns either a synchronous or asynchronous httpx client with optional TLS configuration.
60
60
 
@@ -2,6 +2,9 @@ import logging
2
2
  import sys
3
3
 
4
4
  import structlog
5
+ from structlog.contextvars import merge_contextvars
6
+ from structlog.dev import ConsoleRenderer, set_exc_info
7
+ from structlog.processors import StackInfoRenderer, TimeStamper, add_log_level
5
8
 
6
9
  # Using this module allows us to be consistent with the logging configuration across all Python programs.
7
10
 
@@ -24,9 +27,40 @@ def configure_logging_early():
24
27
  )
25
28
 
26
29
 
27
- def configure_production_logging():
30
+ def configure_development_mode_logging():
28
31
  processors = [
29
- structlog.processors.dict_tracebacks,
32
+ structlog_suppressor,
33
+ merge_contextvars,
34
+ add_log_level,
35
+ StackInfoRenderer(),
36
+ set_exc_info,
37
+ TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False),
38
+ ConsoleRenderer(),
39
+ ]
40
+ structlog.configure(
41
+ processors=processors,
42
+ )
43
+
44
+
45
+ def configure_production_mode_logging():
46
+ processors = [
47
+ structlog_suppressor,
48
+ structlog.processors.format_exc_info,
30
49
  structlog.processors.JSONRenderer(),
31
50
  ]
32
51
  structlog.configure(processors=processors)
52
+
53
+
54
+ _suppress_logging = False
55
+
56
+
57
+ def structlog_suppressor(logger, name, event_dict):
58
+ global _suppress_logging
59
+ return None if _suppress_logging else event_dict
60
+
61
+
62
+ def suppress():
63
+ """Sets the log level for the root logger to the new level."""
64
+ global _suppress_logging
65
+ _suppress_logging = True
66
+ logging.getLogger().setLevel(logging.CRITICAL)
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.3
2
+ Name: indexify
3
+ Version: 0.3.1
4
+ Summary: Open Source Indexify components and helper tools
5
+ Home-page: https://github.com/tensorlakeai/indexify
6
+ License: Apache 2.0
7
+ Author: Tensorlake Inc.
8
+ Author-email: support@tensorlake.ai
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: License :: Other/Proprietary License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Dist: grpcio (==1.68.1)
18
+ Requires-Dist: grpcio-tools (==1.68.1)
19
+ Requires-Dist: httpx-sse (>=0.4.0,<0.5.0)
20
+ Requires-Dist: httpx[http2] (>=0.28.1,<0.29.0)
21
+ Requires-Dist: nanoid (>=2.0.0,<3.0.0)
22
+ Requires-Dist: pydantic (==2.10.4)
23
+ Requires-Dist: pyyaml (>=6,<7)
24
+ Requires-Dist: rich (>=13.9.2,<14.0.0)
25
+ Requires-Dist: structlog (>=24.4.0,<25.0.0)
26
+ Requires-Dist: tensorlake (>=0.1.7,<0.2.0)
27
+ Requires-Dist: typer (>=0.12,<0.13)
28
+ Project-URL: Repository, https://github.com/tensorlakeai/indexify
29
+ Description-Content-Type: text/markdown
30
+
31
+ ## Overview
32
+
33
+ This a package with all Open Source Indexify components and helper tools
34
+ available via a CLI.
35
+
36
+ The CLI allows to:
37
+ * Setup a local or a distributed Indexify cluster.
38
+ * Build container images for Indexify functions.
@@ -0,0 +1,44 @@
1
+ indexify/cli/cli.py,sha256=II73vJlVeEy51pPLnrnBcYJbZQxCPItkFRFEN8MD37c,9122
2
+ indexify/executor/README.md,sha256=ozC6_hMkhQQNVCMEpBxwiUALz6lwErPQxNxQfQDqnG4,2029
3
+ indexify/executor/api_objects.py,sha256=QeXOZpS7nkVA_dsRQFlS3EuMvqFvLGaw1Spcr0c-5lA,1015
4
+ indexify/executor/downloader.py,sha256=Vrq1dAW4BifG62tlqFnImiMxdezIgOZcByTRnhDsnnw,6457
5
+ indexify/executor/executor.py,sha256=lln0p0cVN2aNY3bsHFp7Phbaa2q9gnQABi1xEPxo1Nc,5859
6
+ indexify/executor/function_executor/function_executor.py,sha256=a9pAWQWDLJL-i5bp1PEMD2eMYOTvXAh_1jQsXcL_JCQ,5226
7
+ indexify/executor/function_executor/function_executor_state.py,sha256=_85dpaudYM0sekOqwjMxKGdK7MNQdTGUhHi67sqVHyY,2853
8
+ indexify/executor/function_executor/invocation_state_client.py,sha256=jxElxnYFfdwCxKfKvQsjLIE-Z_1386wxednJ2pGKSL0,8581
9
+ indexify/executor/function_executor/server/function_executor_server.py,sha256=_DLivLDikupZusRk8gVWDk7fWPT9XjZ4un1yWSlOObs,883
10
+ indexify/executor/function_executor/server/function_executor_server_factory.py,sha256=pGbJMQfC5TNvyWOs6VDKdqd2PK5OHQh5_wSDP-E7DbI,1677
11
+ indexify/executor/function_executor/server/subprocess_function_executor_server.py,sha256=DxQMwAvQi03OGo2sFruUrxwcMPG8In4C62L3sped2FI,694
12
+ indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py,sha256=cVr2wXQL5zVlq-Ep44ZBXBFCQbrbZSMscjRmzVDakvk,4295
13
+ indexify/executor/function_executor/single_task_runner.py,sha256=NGji5iIoA9vxRzSvyUTL3Gs5g4HMq5b1LYN5ZsENcls,6244
14
+ indexify/executor/function_executor/task_input.py,sha256=8pBRuaQbBBPrDWwByOKZlOaXu6UDmJUO5w-6XxAYSgY,582
15
+ indexify/executor/function_executor/task_output.py,sha256=3-qcT1aigmOFEp6QSJtUcQfqeJfrxvo265qgk9WAUVg,1064
16
+ indexify/executor/runtime_probes.py,sha256=bo6Dq6AGZpJH099j0DHtVSDEH80tv3j9MXf3VXSx_p8,2182
17
+ indexify/executor/task_fetcher.py,sha256=Cpm-LRZz1MSfW-xHJbIaKc1wxzRhIrud9GTOIxWODt0,2883
18
+ indexify/executor/task_reporter.py,sha256=eueJNHYJIwAm8lY8SQJrUOWC-Nk-DPesDis1YTR4k7c,6643
19
+ indexify/executor/task_runner.py,sha256=RSFeJYhQ_agXXPBm8u13HErSUPZGsCGwV1stgS7g258,5035
20
+ indexify/function_executor/README.md,sha256=TAcbYYe6noUMriaQabJAaZ8a2SAYzlXl7hIdTfULqYY,950
21
+ indexify/function_executor/handlers/run_function/function_inputs_loader.py,sha256=StSJCp-kkOWUuUIzONnoqtcB37nzBYGcIVy2oTEwOIQ,1588
22
+ indexify/function_executor/handlers/run_function/handler.py,sha256=DY_aIR8MZsa1cnFxbVEHhcxQSqBwW4cYRN9NhzuDDFo,5090
23
+ indexify/function_executor/handlers/run_function/request_validator.py,sha256=GS7_zxAlyNg0vpUrhot-DpVWFyI9Y9P8jvb7UuO4b1A,856
24
+ indexify/function_executor/handlers/run_function/response_helper.py,sha256=FJVl-LyoRBx9HRaOo_umveiWzuh4anO7aXUmEMEki20,3074
25
+ indexify/function_executor/initialize_request_validator.py,sha256=agYWGRHUC-7IuYtm9NPjcVo5SVOWM4PxewA93dqdkJM,682
26
+ indexify/function_executor/invocation_state/invocation_state_proxy_server.py,sha256=YEnxvWV8NFVK_CxHI_C-BNizuagalY3ieN62IOxNttU,7023
27
+ indexify/function_executor/invocation_state/proxied_invocation_state.py,sha256=rjZ2192bSXonNKkaDaByeOL-wVx6x6Qrtio0UVzGSz8,944
28
+ indexify/function_executor/invocation_state/response_validator.py,sha256=05Y9_5s010owaDb6OUia9W9Alx1r1zJ179HboEkT-Xk,896
29
+ indexify/function_executor/main.py,sha256=uOzVUhl4LzGJ9Cw3kyyFddaEIMxRETEKFHPnvJVtc50,1152
30
+ indexify/function_executor/proto/configuration.py,sha256=oHp_Hh1_XdL4v7M2Ok-Z9r7ClTxiWdSP1Ip-NK4Ee6E,1096
31
+ indexify/function_executor/proto/function_executor.proto,sha256=Ae6vMCNnacHWmrF6aZVPzIVVzG5sJgP7LXV4vIUBKZM,3978
32
+ indexify/function_executor/proto/function_executor_pb2.py,sha256=X2G2xZQy8VRWqlDKV2dUgD3CkEecFx6Jp2EuTuXrx9o,7358
33
+ indexify/function_executor/proto/function_executor_pb2.pyi,sha256=TQEOl3W1s4fxtp1g8Hi5S-keMF25DY5-6miyDVHhBkE,7168
34
+ indexify/function_executor/proto/function_executor_pb2_grpc.py,sha256=xrqIDrcl1PTyQTGAAGrgSDKEtZilC445Op0tJ7LmS5Q,8716
35
+ indexify/function_executor/proto/message_validator.py,sha256=OKXPYgy5L9c-spnV9Zjv7PA_yxwzvykfhbYylYx8cwQ,1456
36
+ indexify/function_executor/server.py,sha256=tJzUy_v4BT8Le9G3hgtiuDJo9YVFkAU2dVISSsX36II,1061
37
+ indexify/function_executor/service.py,sha256=3JYcMcWiCgvxXhRPReUUBjTBNLIqUbhhsGZIsqIcVw4,5343
38
+ indexify/utils/README.md,sha256=2g8-H9GopacOW4YrViZc0QsaJPtK-Fox7GyfX01kcDk,86
39
+ indexify/utils/http_client.py,sha256=deMlmAu4E_ZXV3blCdWNag3uO_cyD-GsMZjFFmO5r7s,3541
40
+ indexify/utils/logging.py,sha256=c6NwzY7uVHMRJc8f2w2KF36rNkeZVoQfGdq7suIg9s8,2025
41
+ indexify-0.3.1.dist-info/METADATA,sha256=4UcqDjQRLNcVGFezaIROqWdtJxCsPP0hLq8-hlFvDn0,1383
42
+ indexify-0.3.1.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
43
+ indexify-0.3.1.dist-info/entry_points.txt,sha256=pJG0YRnypesbiNJHuObfHEkjk0p_ZvEDTyyTH0kGVTY,108
44
+ indexify-0.3.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: poetry-core 2.0.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ function-executor=indexify.function_executor.main:main
3
+ indexify-cli=indexify.cli.cli:app
4
+
indexify/__init__.py DELETED
@@ -1,31 +0,0 @@
1
- from . import data_loaders
2
- from .functions_sdk.graph import Graph
3
- from .functions_sdk.image import Image
4
- from .functions_sdk.indexify_functions import (
5
- IndexifyFunction,
6
- IndexifyRouter,
7
- get_ctx,
8
- indexify_function,
9
- indexify_router,
10
- )
11
- from .functions_sdk.pipeline import Pipeline
12
- from .http_client import IndexifyClient
13
- from .remote_graph import RemoteGraph
14
- from .remote_pipeline import RemotePipeline
15
- from .settings import DEFAULT_SERVICE_URL
16
-
17
- __all__ = [
18
- "data_loaders",
19
- "Graph",
20
- "RemoteGraph",
21
- "Pipeline",
22
- "RemotePipeline",
23
- "Image",
24
- "indexify_function",
25
- "get_ctx",
26
- "IndexifyFunction",
27
- "IndexifyRouter",
28
- "indexify_router",
29
- "DEFAULT_SERVICE_URL",
30
- "IndexifyClient",
31
- ]
@@ -1,58 +0,0 @@
1
- import hashlib
2
- import mimetypes
3
- import os
4
- from abc import ABC, abstractmethod
5
- from typing import List
6
-
7
- from pydantic import BaseModel
8
-
9
-
10
- class FileMetadata(BaseModel):
11
- path: str
12
- file_size: int
13
- mime_type: str
14
- md5_hash: str
15
- created_at: int
16
- updated_at: int
17
-
18
- @classmethod
19
- def from_path(cls, path: str):
20
- file_size = os.path.getsize(path)
21
- mime_type = mimetypes.guess_type(path)[0]
22
-
23
- # Compute MD5 hash
24
- hash_md5 = hashlib.md5()
25
- with open(path, "rb") as f:
26
- for chunk in iter(lambda: f.read(4096), b""):
27
- hash_md5.update(chunk)
28
- md5_hash = hash_md5.hexdigest()
29
-
30
- created_at = int(os.path.getctime(path))
31
- updated_at = int(os.path.getmtime(path))
32
-
33
- return cls(
34
- path=path,
35
- file_size=file_size,
36
- mime_type=str(mime_type),
37
- md5_hash=md5_hash,
38
- created_at=created_at,
39
- updated_at=updated_at,
40
- )
41
-
42
-
43
- class DataLoader(ABC):
44
- @abstractmethod
45
- def load(self) -> List[FileMetadata]:
46
- pass
47
-
48
- @abstractmethod
49
- def read_all_bytes(self, file_metadata: FileMetadata) -> bytes:
50
- pass
51
-
52
- @abstractmethod
53
- def state(self) -> dict:
54
- pass
55
-
56
-
57
- from .local_directory_loader import LocalDirectoryLoader
58
- from .url_loader import UrlLoader
@@ -1,37 +0,0 @@
1
- import os
2
- from typing import List, Optional
3
-
4
- from . import DataLoader, FileMetadata
5
-
6
-
7
- class LocalDirectoryLoader(DataLoader):
8
- def __init__(
9
- self,
10
- directory: str,
11
- file_extensions: Optional[List[str]] = None,
12
- state: dict = {},
13
- ):
14
- self.directory = directory
15
- self.file_extensions = file_extensions
16
- self.processed_files = set(state.get("processed_files", []))
17
-
18
- def load(self) -> List[FileMetadata]:
19
- file_metadata_list = []
20
- for root, _, files in os.walk(self.directory):
21
- for file in files:
22
- if self.file_extensions is None or any(
23
- file.endswith(ext) for ext in self.file_extensions
24
- ):
25
- file_path = os.path.join(root, file)
26
- if file_path not in self.processed_files:
27
- file_metadata_list.append(FileMetadata.from_path(file_path))
28
- self.processed_files.add(file_path)
29
-
30
- return file_metadata_list
31
-
32
- def read_all_bytes(self, file: FileMetadata) -> bytes:
33
- with open(file.path, "rb") as f:
34
- return f.read()
35
-
36
- def state(self) -> dict:
37
- return {"processed_files": list(self.processed_files)}
@@ -1,52 +0,0 @@
1
- import email.utils
2
- from typing import List
3
-
4
- import httpx
5
-
6
- from . import DataLoader, FileMetadata
7
-
8
-
9
- def convert_date_to_epoch(date_str: str) -> int:
10
- """
11
- Convert a date string from URL header to Unix epoch time.
12
-
13
- Args:
14
- date_str (str): The date string from the URL header.
15
-
16
- Returns:
17
- int: The Unix epoch time.
18
- """
19
- if not date_str:
20
- return 0
21
- parsed_date = email.utils.parsedate_to_datetime(date_str)
22
- return int(parsed_date.timestamp())
23
-
24
-
25
- class UrlLoader(DataLoader):
26
- def __init__(self, urls: List[str], state: dict = {}):
27
- self.urls = urls
28
-
29
- def load(self) -> List[FileMetadata]:
30
- file_metadata_list = []
31
- for url in self.urls:
32
- response = httpx.head(url, follow_redirects=True)
33
- file_metadata_list.append(
34
- FileMetadata(
35
- path=url,
36
- file_size=response.headers.get("content-length", 0),
37
- mime_type=response.headers.get("content-type"),
38
- md5_hash="",
39
- created_at=convert_date_to_epoch(response.headers.get("date")),
40
- updated_at=convert_date_to_epoch(
41
- response.headers.get("last-modified")
42
- ),
43
- )
44
- )
45
- return file_metadata_list
46
-
47
- def read_all_bytes(self, file: FileMetadata) -> bytes:
48
- response = httpx.get(file.path, follow_redirects=True)
49
- return response.content
50
-
51
- def state(self) -> dict:
52
- return {}
indexify/error.py DELETED
@@ -1,8 +0,0 @@
1
- class ApiException(Exception):
2
- def __init__(self, message: str) -> None:
3
- super().__init__(message)
4
-
5
-
6
- class GraphStillProcessing(Exception):
7
- def __init__(self) -> None:
8
- super().__init__("graph is still processing")