indexify 0.2.40__py3-none-any.whl → 0.2.42__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 (42) hide show
  1. indexify/cli.py +92 -52
  2. indexify/executor/agent.py +99 -187
  3. indexify/executor/api_objects.py +2 -8
  4. indexify/executor/downloader.py +129 -90
  5. indexify/executor/executor_tasks.py +15 -30
  6. indexify/executor/function_executor/function_executor.py +32 -0
  7. indexify/executor/function_executor/function_executor_factory.py +26 -0
  8. indexify/executor/function_executor/function_executor_map.py +91 -0
  9. indexify/executor/function_executor/process_function_executor.py +64 -0
  10. indexify/executor/function_executor/process_function_executor_factory.py +102 -0
  11. indexify/executor/function_worker.py +227 -184
  12. indexify/executor/runtime_probes.py +9 -8
  13. indexify/executor/task_fetcher.py +80 -0
  14. indexify/executor/task_reporter.py +18 -25
  15. indexify/executor/task_store.py +35 -16
  16. indexify/function_executor/function_executor_service.py +86 -0
  17. indexify/function_executor/handlers/run_function/function_inputs_loader.py +54 -0
  18. indexify/function_executor/handlers/run_function/handler.py +149 -0
  19. indexify/function_executor/handlers/run_function/request_validator.py +24 -0
  20. indexify/function_executor/handlers/run_function/response_helper.py +98 -0
  21. indexify/function_executor/initialize_request_validator.py +22 -0
  22. indexify/function_executor/proto/configuration.py +13 -0
  23. indexify/function_executor/proto/function_executor.proto +70 -0
  24. indexify/function_executor/proto/function_executor_pb2.py +53 -0
  25. indexify/function_executor/proto/function_executor_pb2.pyi +125 -0
  26. indexify/function_executor/proto/function_executor_pb2_grpc.py +163 -0
  27. indexify/function_executor/proto/message_validator.py +38 -0
  28. indexify/function_executor/server.py +31 -0
  29. indexify/functions_sdk/data_objects.py +0 -9
  30. indexify/functions_sdk/graph.py +10 -11
  31. indexify/functions_sdk/graph_definition.py +2 -2
  32. indexify/functions_sdk/image.py +35 -30
  33. indexify/functions_sdk/indexify_functions.py +5 -5
  34. indexify/http_client.py +15 -23
  35. indexify/logging.py +32 -0
  36. {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/METADATA +3 -1
  37. indexify-0.2.42.dist-info/RECORD +53 -0
  38. indexify/executor/indexify_executor.py +0 -32
  39. indexify-0.2.40.dist-info/RECORD +0 -34
  40. {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/LICENSE.txt +0 -0
  41. {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/WHEEL +0 -0
  42. {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: indexify/function_executor/proto/function_executor.proto
5
+ # Protobuf Python Version: 5.28.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+
13
+ _runtime_version.ValidateProtobufRuntimeVersion(
14
+ _runtime_version.Domain.PUBLIC,
15
+ 5,
16
+ 28,
17
+ 1,
18
+ "",
19
+ "indexify/function_executor/proto/function_executor.proto",
20
+ )
21
+ # @@protoc_insertion_point(imports)
22
+
23
+ _sym_db = _symbol_database.Default()
24
+
25
+
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"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\xe0\x01\n\x10\x46unctionExecutor\x12i\n\ninitialize\x12,.function_executor_service.InitializeRequest\x1a-.function_executor_service.InitializeResponse\x12\x61\n\x08run_task\x12).function_executor_service.RunTaskRequest\x1a*.function_executor_service.RunTaskResponseb\x06proto3'
28
+ )
29
+
30
+ _globals = globals()
31
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
32
+ _builder.BuildTopDescriptorsAndMessages(
33
+ DESCRIPTOR, "indexify.function_executor.proto.function_executor_pb2", _globals
34
+ )
35
+ if not _descriptor._USE_C_DESCRIPTORS:
36
+ DESCRIPTOR._loaded_options = None
37
+ _globals["_SERIALIZEDOBJECT"]._serialized_start = 87
38
+ _globals["_SERIALIZEDOBJECT"]._serialized_end = 192
39
+ _globals["_INITIALIZEREQUEST"]._serialized_start = 195
40
+ _globals["_INITIALIZEREQUEST"]._serialized_end = 459
41
+ _globals["_INITIALIZERESPONSE"]._serialized_start = 461
42
+ _globals["_INITIALIZERESPONSE"]._serialized_end = 515
43
+ _globals["_FUNCTIONOUTPUT"]._serialized_start = 517
44
+ _globals["_FUNCTIONOUTPUT"]._serialized_end = 595
45
+ _globals["_ROUTEROUTPUT"]._serialized_start = 597
46
+ _globals["_ROUTEROUTPUT"]._serialized_end = 626
47
+ _globals["_RUNTASKREQUEST"]._serialized_start = 629
48
+ _globals["_RUNTASKREQUEST"]._serialized_end = 933
49
+ _globals["_RUNTASKRESPONSE"]._serialized_start = 936
50
+ _globals["_RUNTASKRESPONSE"]._serialized_end = 1305
51
+ _globals["_FUNCTIONEXECUTOR"]._serialized_start = 1308
52
+ _globals["_FUNCTIONEXECUTOR"]._serialized_end = 1532
53
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,125 @@
1
+ from typing import ClassVar as _ClassVar
2
+ from typing import Iterable as _Iterable
3
+ from typing import Mapping as _Mapping
4
+ from typing import Optional as _Optional
5
+ from typing import Union as _Union
6
+
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import message as _message
9
+ from google.protobuf.internal import containers as _containers
10
+
11
+ DESCRIPTOR: _descriptor.FileDescriptor
12
+
13
+ class SerializedObject(_message.Message):
14
+ __slots__ = ("bytes", "string", "content_type")
15
+ BYTES_FIELD_NUMBER: _ClassVar[int]
16
+ STRING_FIELD_NUMBER: _ClassVar[int]
17
+ CONTENT_TYPE_FIELD_NUMBER: _ClassVar[int]
18
+ bytes: bytes
19
+ string: str
20
+ content_type: str
21
+ def __init__(
22
+ self,
23
+ bytes: _Optional[bytes] = ...,
24
+ string: _Optional[str] = ...,
25
+ content_type: _Optional[str] = ...,
26
+ ) -> None: ...
27
+
28
+ class InitializeRequest(_message.Message):
29
+ __slots__ = ("namespace", "graph_name", "graph_version", "function_name", "graph")
30
+ NAMESPACE_FIELD_NUMBER: _ClassVar[int]
31
+ GRAPH_NAME_FIELD_NUMBER: _ClassVar[int]
32
+ GRAPH_VERSION_FIELD_NUMBER: _ClassVar[int]
33
+ FUNCTION_NAME_FIELD_NUMBER: _ClassVar[int]
34
+ GRAPH_FIELD_NUMBER: _ClassVar[int]
35
+ namespace: str
36
+ graph_name: str
37
+ graph_version: int
38
+ function_name: str
39
+ graph: SerializedObject
40
+ def __init__(
41
+ self,
42
+ namespace: _Optional[str] = ...,
43
+ graph_name: _Optional[str] = ...,
44
+ graph_version: _Optional[int] = ...,
45
+ function_name: _Optional[str] = ...,
46
+ graph: _Optional[_Union[SerializedObject, _Mapping]] = ...,
47
+ ) -> None: ...
48
+
49
+ class InitializeResponse(_message.Message):
50
+ __slots__ = ("success",)
51
+ SUCCESS_FIELD_NUMBER: _ClassVar[int]
52
+ success: bool
53
+ def __init__(self, success: bool = ...) -> None: ...
54
+
55
+ class FunctionOutput(_message.Message):
56
+ __slots__ = ("outputs",)
57
+ OUTPUTS_FIELD_NUMBER: _ClassVar[int]
58
+ outputs: _containers.RepeatedCompositeFieldContainer[SerializedObject]
59
+ def __init__(
60
+ self, outputs: _Optional[_Iterable[_Union[SerializedObject, _Mapping]]] = ...
61
+ ) -> None: ...
62
+
63
+ class RouterOutput(_message.Message):
64
+ __slots__ = ("edges",)
65
+ EDGES_FIELD_NUMBER: _ClassVar[int]
66
+ edges: _containers.RepeatedScalarFieldContainer[str]
67
+ def __init__(self, edges: _Optional[_Iterable[str]] = ...) -> None: ...
68
+
69
+ class RunTaskRequest(_message.Message):
70
+ __slots__ = (
71
+ "graph_invocation_id",
72
+ "task_id",
73
+ "function_input",
74
+ "function_init_value",
75
+ )
76
+ GRAPH_INVOCATION_ID_FIELD_NUMBER: _ClassVar[int]
77
+ TASK_ID_FIELD_NUMBER: _ClassVar[int]
78
+ FUNCTION_INPUT_FIELD_NUMBER: _ClassVar[int]
79
+ FUNCTION_INIT_VALUE_FIELD_NUMBER: _ClassVar[int]
80
+ graph_invocation_id: str
81
+ task_id: str
82
+ function_input: SerializedObject
83
+ function_init_value: SerializedObject
84
+ def __init__(
85
+ self,
86
+ graph_invocation_id: _Optional[str] = ...,
87
+ task_id: _Optional[str] = ...,
88
+ function_input: _Optional[_Union[SerializedObject, _Mapping]] = ...,
89
+ function_init_value: _Optional[_Union[SerializedObject, _Mapping]] = ...,
90
+ ) -> None: ...
91
+
92
+ class RunTaskResponse(_message.Message):
93
+ __slots__ = (
94
+ "task_id",
95
+ "function_output",
96
+ "router_output",
97
+ "stdout",
98
+ "stderr",
99
+ "is_reducer",
100
+ "success",
101
+ )
102
+ TASK_ID_FIELD_NUMBER: _ClassVar[int]
103
+ FUNCTION_OUTPUT_FIELD_NUMBER: _ClassVar[int]
104
+ ROUTER_OUTPUT_FIELD_NUMBER: _ClassVar[int]
105
+ STDOUT_FIELD_NUMBER: _ClassVar[int]
106
+ STDERR_FIELD_NUMBER: _ClassVar[int]
107
+ IS_REDUCER_FIELD_NUMBER: _ClassVar[int]
108
+ SUCCESS_FIELD_NUMBER: _ClassVar[int]
109
+ task_id: str
110
+ function_output: FunctionOutput
111
+ router_output: RouterOutput
112
+ stdout: str
113
+ stderr: str
114
+ is_reducer: bool
115
+ success: bool
116
+ def __init__(
117
+ self,
118
+ task_id: _Optional[str] = ...,
119
+ function_output: _Optional[_Union[FunctionOutput, _Mapping]] = ...,
120
+ router_output: _Optional[_Union[RouterOutput, _Mapping]] = ...,
121
+ stdout: _Optional[str] = ...,
122
+ stderr: _Optional[str] = ...,
123
+ is_reducer: bool = ...,
124
+ success: bool = ...,
125
+ ) -> None: ...
@@ -0,0 +1,163 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import warnings
4
+
5
+ import grpc
6
+
7
+ from indexify.function_executor.proto import (
8
+ function_executor_pb2 as indexify_dot_function__executor_dot_proto_dot_function__executor__pb2,
9
+ )
10
+
11
+ GRPC_GENERATED_VERSION = "1.68.1"
12
+ GRPC_VERSION = grpc.__version__
13
+ _version_not_supported = False
14
+
15
+ try:
16
+ from grpc._utilities import first_version_is_lower
17
+
18
+ _version_not_supported = first_version_is_lower(
19
+ GRPC_VERSION, GRPC_GENERATED_VERSION
20
+ )
21
+ except ImportError:
22
+ _version_not_supported = True
23
+
24
+ if _version_not_supported:
25
+ raise RuntimeError(
26
+ f"The grpc package installed is at version {GRPC_VERSION},"
27
+ + f" but the generated code in indexify/function_executor/proto/function_executor_pb2_grpc.py depends on"
28
+ + f" grpcio>={GRPC_GENERATED_VERSION}."
29
+ + f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}"
30
+ + f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}."
31
+ )
32
+
33
+
34
+ class FunctionExecutorStub(object):
35
+ """Missing associated documentation comment in .proto file."""
36
+
37
+ def __init__(self, channel):
38
+ """Constructor.
39
+
40
+ Args:
41
+ channel: A grpc.Channel.
42
+ """
43
+ self.initialize = channel.unary_unary(
44
+ "/function_executor_service.FunctionExecutor/initialize",
45
+ request_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.InitializeRequest.SerializeToString,
46
+ response_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.InitializeResponse.FromString,
47
+ _registered_method=True,
48
+ )
49
+ self.run_task = channel.unary_unary(
50
+ "/function_executor_service.FunctionExecutor/run_task",
51
+ request_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskRequest.SerializeToString,
52
+ response_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskResponse.FromString,
53
+ _registered_method=True,
54
+ )
55
+
56
+
57
+ class FunctionExecutorServicer(object):
58
+ """Missing associated documentation comment in .proto file."""
59
+
60
+ def initialize(self, request, context):
61
+ """Initializes the Function Executor to run tasks
62
+ for a particular function. This method is called only
63
+ once per Function Executor as it can only run a single function.
64
+ It should be called before calling RunTask for the function.
65
+ """
66
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
67
+ context.set_details("Method not implemented!")
68
+ raise NotImplementedError("Method not implemented!")
69
+
70
+ def run_task(self, request, context):
71
+ """Executes the task defined in the request.
72
+ Multiple tasks can be running in parallel.
73
+ """
74
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
75
+ context.set_details("Method not implemented!")
76
+ raise NotImplementedError("Method not implemented!")
77
+
78
+
79
+ def add_FunctionExecutorServicer_to_server(servicer, server):
80
+ rpc_method_handlers = {
81
+ "initialize": grpc.unary_unary_rpc_method_handler(
82
+ servicer.initialize,
83
+ request_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.InitializeRequest.FromString,
84
+ response_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.InitializeResponse.SerializeToString,
85
+ ),
86
+ "run_task": grpc.unary_unary_rpc_method_handler(
87
+ servicer.run_task,
88
+ request_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskRequest.FromString,
89
+ response_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskResponse.SerializeToString,
90
+ ),
91
+ }
92
+ generic_handler = grpc.method_handlers_generic_handler(
93
+ "function_executor_service.FunctionExecutor", rpc_method_handlers
94
+ )
95
+ server.add_generic_rpc_handlers((generic_handler,))
96
+ server.add_registered_method_handlers(
97
+ "function_executor_service.FunctionExecutor", rpc_method_handlers
98
+ )
99
+
100
+
101
+ # This class is part of an EXPERIMENTAL API.
102
+ class FunctionExecutor(object):
103
+ """Missing associated documentation comment in .proto file."""
104
+
105
+ @staticmethod
106
+ def initialize(
107
+ request,
108
+ target,
109
+ options=(),
110
+ channel_credentials=None,
111
+ call_credentials=None,
112
+ insecure=False,
113
+ compression=None,
114
+ wait_for_ready=None,
115
+ timeout=None,
116
+ metadata=None,
117
+ ):
118
+ return grpc.experimental.unary_unary(
119
+ request,
120
+ target,
121
+ "/function_executor_service.FunctionExecutor/initialize",
122
+ indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.InitializeRequest.SerializeToString,
123
+ indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.InitializeResponse.FromString,
124
+ options,
125
+ channel_credentials,
126
+ insecure,
127
+ call_credentials,
128
+ compression,
129
+ wait_for_ready,
130
+ timeout,
131
+ metadata,
132
+ _registered_method=True,
133
+ )
134
+
135
+ @staticmethod
136
+ def run_task(
137
+ request,
138
+ target,
139
+ options=(),
140
+ channel_credentials=None,
141
+ call_credentials=None,
142
+ insecure=False,
143
+ compression=None,
144
+ wait_for_ready=None,
145
+ timeout=None,
146
+ metadata=None,
147
+ ):
148
+ return grpc.experimental.unary_unary(
149
+ request,
150
+ target,
151
+ "/function_executor_service.FunctionExecutor/run_task",
152
+ indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskRequest.SerializeToString,
153
+ indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskResponse.FromString,
154
+ options,
155
+ channel_credentials,
156
+ insecure,
157
+ call_credentials,
158
+ compression,
159
+ wait_for_ready,
160
+ timeout,
161
+ metadata,
162
+ _registered_method=True,
163
+ )
@@ -0,0 +1,38 @@
1
+ from typing import Any
2
+
3
+ from .function_executor_pb2 import SerializedObject
4
+
5
+
6
+ class MessageValidator:
7
+ def __init__(self, message: Any):
8
+ self._message = message
9
+
10
+ def required_field(self, field_name: str) -> "MessageValidator":
11
+ if not self._message.HasField(field_name):
12
+ raise ValueError(
13
+ f"Field '{field_name}' is required in {type(self._message).__name__}"
14
+ )
15
+ return self
16
+
17
+ def required_serialized_object(self, field_name: str) -> "MessageValidator":
18
+ """Validates the SerializedObject.
19
+
20
+ Raises: ValueError: If the SerializedObject is invalid or not present."""
21
+ self.required_field(field_name)
22
+ return self.optional_serialized_object(field_name)
23
+
24
+ def optional_serialized_object(self, field_name: str) -> "MessageValidator":
25
+ """Validates the SerializedObject.
26
+
27
+ Raises: ValueError: If the SerializedObject is invalid."""
28
+ if not self._message.HasField(field_name):
29
+ return self
30
+
31
+ serializedObject: SerializedObject = getattr(self._message, field_name)
32
+ if not serializedObject.HasField("string") and not serializedObject.HasField(
33
+ "bytes"
34
+ ):
35
+ raise ValueError("oneof 'data' is required in SerializedObject")
36
+ if not serializedObject.HasField("content_type"):
37
+ raise ValueError("Field 'content_type' is required in SerializedObject")
38
+ return self
@@ -0,0 +1,31 @@
1
+ from concurrent.futures import ThreadPoolExecutor
2
+
3
+ import grpc
4
+
5
+ from .function_executor_service import FunctionExecutorService
6
+ from .proto.configuration import GRPC_SERVER_OPTIONS
7
+ from .proto.function_executor_pb2_grpc import (
8
+ add_FunctionExecutorServicer_to_server,
9
+ )
10
+
11
+ # Temporary limit until we have a better way to control this.
12
+ # This limits the number of concurrent tasks that Function Executor can run.
13
+ MAX_RPC_CONCURRENCY = 100
14
+
15
+
16
+ class Server:
17
+ def __init__(self, server_address: str, service: FunctionExecutorService):
18
+ self._server_address: str = server_address
19
+ self._service: FunctionExecutorService = service
20
+
21
+ def run(self):
22
+ """Runs Function Executor Service at the configured address."""
23
+ server = grpc.server(
24
+ thread_pool=ThreadPoolExecutor(max_workers=MAX_RPC_CONCURRENCY),
25
+ maximum_concurrent_rpcs=MAX_RPC_CONCURRENCY,
26
+ options=GRPC_SERVER_OPTIONS,
27
+ )
28
+ add_FunctionExecutorServicer_to_server(self._service, server)
29
+ server.add_insecure_port(self._server_address)
30
+ server.start()
31
+ server.wait_for_termination()
@@ -20,15 +20,6 @@ class IndexifyData(BaseModel):
20
20
  encoder: Literal["cloudpickle", "json"] = "cloudpickle"
21
21
 
22
22
 
23
- class FunctionWorkerOutput(BaseModel):
24
- fn_outputs: Optional[List[IndexifyData]]
25
- router_output: Optional[RouterOutput]
26
- stdout: Optional[str]
27
- stderr: Optional[str]
28
- reducer: bool = False
29
- success: bool = True
30
-
31
-
32
23
  class File(BaseModel):
33
24
  data: bytes
34
25
  mime_type: Optional[str] = None
@@ -1,4 +1,4 @@
1
- import json
1
+ import importlib
2
2
  import sys
3
3
  from collections import defaultdict
4
4
  from queue import deque
@@ -174,7 +174,6 @@ class Graph:
174
174
  fn_name=start_node.name,
175
175
  description=start_node.description,
176
176
  reducer=is_reducer,
177
- image_name=start_node.image._image_name,
178
177
  image_information=start_node.image.to_image_information(),
179
178
  input_encoder=start_node.input_encoder,
180
179
  output_encoder=start_node.output_encoder,
@@ -191,7 +190,6 @@ class Graph:
191
190
  target_fns=self.routers[node_name],
192
191
  input_encoder=node.input_encoder,
193
192
  output_encoder=node.output_encoder,
194
- image_name=node.image._image_name,
195
193
  image_information=node.image.to_image_information(),
196
194
  )
197
195
  )
@@ -202,15 +200,12 @@ class Graph:
202
200
  fn_name=node.name,
203
201
  description=node.description,
204
202
  reducer=node.accumulate is not None,
205
- image_name=node.image._image_name,
206
203
  image_information=node.image.to_image_information(),
207
204
  input_encoder=node.input_encoder,
208
205
  output_encoder=node.output_encoder,
209
206
  )
210
207
  )
211
208
 
212
- print("TAGS", self.tags)
213
-
214
209
  return ComputeGraphMetadata(
215
210
  name=self.name,
216
211
  description=self.description or "",
@@ -221,6 +216,7 @@ class Graph:
221
216
  runtime_information=RuntimeInformation(
222
217
  major_version=sys.version_info.major,
223
218
  minor_version=sys.version_info.minor,
219
+ sdk_version=importlib.metadata.version("indexify"),
224
220
  ),
225
221
  )
226
222
 
@@ -265,9 +261,14 @@ class Graph:
265
261
  current_node_name = queue.popleft()
266
262
  neighbours = (
267
263
  self.edges[current_node_name]
268
- if self.edges[current_node_name]
269
- else self.routers[current_node_name]
264
+ if current_node_name in self.edges
265
+ else (
266
+ self.routers[current_node_name]
267
+ if current_node_name in self.routers
268
+ else []
269
+ )
270
270
  )
271
+
271
272
  for neighbour in neighbours:
272
273
  if neighbour in visited:
273
274
  continue
@@ -277,9 +278,7 @@ class Graph:
277
278
 
278
279
  if total_number_of_nodes != len(visited):
279
280
  # all the nodes are not reachable from the start_node.
280
- raise Exception(
281
- "Some nodes in the graph are not reachable from start node."
282
- )
281
+ raise Exception("Some nodes in the graph are not reachable from start node")
283
282
 
284
283
  def _run(
285
284
  self,
@@ -12,7 +12,6 @@ class FunctionMetadata(BaseModel):
12
12
  fn_name: str
13
13
  description: str
14
14
  reducer: bool = False
15
- image_name: str
16
15
  image_information: ImageInformation
17
16
  input_encoder: str = "cloudpickle"
18
17
  output_encoder: str = "cloudpickle"
@@ -23,7 +22,6 @@ class RouterMetadata(BaseModel):
23
22
  description: str
24
23
  source_fn: str
25
24
  target_fns: List[str]
26
- image_name: str
27
25
  image_information: ImageInformation
28
26
  input_encoder: str = "cloudpickle"
29
27
  output_encoder: str = "cloudpickle"
@@ -38,6 +36,7 @@ class NodeMetadata(BaseModel):
38
36
  class RuntimeInformation(BaseModel):
39
37
  major_version: int
40
38
  minor_version: int
39
+ sdk_version: str
41
40
 
42
41
 
43
42
  class ComputeGraphMetadata(BaseModel):
@@ -50,6 +49,7 @@ class ComputeGraphMetadata(BaseModel):
50
49
  accumulator_zero_values: Dict[str, bytes] = {}
51
50
  runtime_information: RuntimeInformation
52
51
  replaying: bool = False
52
+ version: Optional[int] = -1
53
53
 
54
54
  def get_input_payload_serializer(self):
55
55
  return get_serializer(self.start_node.compute_fn.input_encoder)
@@ -1,34 +1,29 @@
1
- from typing import List
1
+ import hashlib
2
+ import importlib
3
+ import sys
4
+ from typing import List, Optional
2
5
 
3
6
  from pydantic import BaseModel
4
7
 
5
8
 
6
- def python_version_to_image(python_version):
7
- if python_version.startswith("3.9"):
8
- return "python:3.9.20-bookworm"
9
- elif python_version.startswith("3.10"):
10
- return "python:3.10.15-bookworm"
11
- elif python_version.startswith("3.11"):
12
- return "python:3.11.10-bookworm"
13
- else:
14
- raise ValueError(f"unsupported Python version: {python_version}")
15
-
16
-
17
9
  # Pydantic object for API
18
10
  class ImageInformation(BaseModel):
19
11
  image_name: str
20
12
  tag: str
21
13
  base_image: str
22
14
  run_strs: List[str]
15
+ image_url: Optional[str] = ""
16
+ sdk_version: str
23
17
 
24
18
 
25
19
  class Image:
26
- def __init__(self, python="3.10"):
20
+ def __init__(self):
27
21
  self._image_name = None
28
22
  self._tag = "latest"
29
- self._base_image = python_version_to_image(python)
30
- self._python_version = python
23
+ self._base_image = BASE_IMAGE_NAME
24
+ self._python_version = LOCAL_PYTHON_VERSION
31
25
  self._run_strs = []
26
+ self._sdk_version = importlib.metadata.version("indexify")
32
27
 
33
28
  def name(self, image_name):
34
29
  self._image_name = image_name
@@ -52,21 +47,31 @@ class Image:
52
47
  tag=self._tag,
53
48
  base_image=self._base_image,
54
49
  run_strs=self._run_strs,
50
+ sdk_version=self._sdk_version,
55
51
  )
56
52
 
53
+ def hash(self) -> str:
54
+ hash = hashlib.sha256(
55
+ self._image_name.encode()
56
+ ) # Make a hash of the image name
57
+ hash.update(self._base_image.encode())
58
+ hash.update("".join(self._run_strs).encode())
59
+ hash.update(self._sdk_version.encode())
60
+
61
+ return hash.hexdigest()
62
+
63
+
64
+ LOCAL_PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
65
+ BASE_IMAGE_NAME = f"python:{LOCAL_PYTHON_VERSION}-slim-bookworm"
66
+
67
+
68
+ def GetDefaultPythonImage(python_version: str):
69
+ return (
70
+ Image()
71
+ .name("tensorlake/indexify-executor-default")
72
+ .base_image(f"python:{python_version}-slim-bookworm")
73
+ .tag(python_version)
74
+ )
75
+
57
76
 
58
- DEFAULT_IMAGE_3_10 = (
59
- Image()
60
- .name("tensorlake/indexify-executor-default")
61
- .base_image("python:3.10.15-slim-bookworm")
62
- .tag("3.10")
63
- .run("pip install indexify")
64
- )
65
-
66
- DEFAULT_IMAGE_3_11 = (
67
- Image()
68
- .name("tensorlake/indexify-executor-default")
69
- .base_image("python:3.11.10-slim-bookworm")
70
- .tag("3.11")
71
- .run("pip install indexify")
72
- )
77
+ DEFAULT_IMAGE = GetDefaultPythonImage(LOCAL_PYTHON_VERSION)
@@ -16,7 +16,7 @@ from typing import (
16
16
  from pydantic import BaseModel, Field, PrivateAttr
17
17
 
18
18
  from .data_objects import IndexifyData
19
- from .image import DEFAULT_IMAGE_3_10, Image
19
+ from .image import DEFAULT_IMAGE, Image
20
20
  from .object_serializer import get_serializer
21
21
 
22
22
 
@@ -80,7 +80,7 @@ class PlacementConstraints(BaseModel):
80
80
  class IndexifyFunction:
81
81
  name: str = ""
82
82
  description: str = ""
83
- image: Optional[Image] = DEFAULT_IMAGE_3_10
83
+ image: Optional[Image] = DEFAULT_IMAGE
84
84
  placement_constraints: List[PlacementConstraints] = []
85
85
  accumulate: Optional[Type[Any]] = None
86
86
  input_encoder: Optional[str] = "cloudpickle"
@@ -103,7 +103,7 @@ class IndexifyFunction:
103
103
  class IndexifyRouter:
104
104
  name: str = ""
105
105
  description: str = ""
106
- image: Optional[Image] = DEFAULT_IMAGE_3_10
106
+ image: Optional[Image] = DEFAULT_IMAGE
107
107
  placement_constraints: List[PlacementConstraints] = []
108
108
  input_encoder: Optional[str] = "cloudpickle"
109
109
  output_encoder: Optional[str] = "cloudpickle"
@@ -141,7 +141,7 @@ def _process_dict_arg(dict_arg: dict, sig: inspect.Signature) -> Tuple[list, dic
141
141
  def indexify_router(
142
142
  name: Optional[str] = None,
143
143
  description: Optional[str] = "",
144
- image: Optional[Image] = DEFAULT_IMAGE_3_10,
144
+ image: Optional[Image] = DEFAULT_IMAGE,
145
145
  placement_constraints: List[PlacementConstraints] = [],
146
146
  input_encoder: Optional[str] = "cloudpickle",
147
147
  output_encoder: Optional[str] = "cloudpickle",
@@ -188,7 +188,7 @@ def indexify_router(
188
188
  def indexify_function(
189
189
  name: Optional[str] = None,
190
190
  description: Optional[str] = "",
191
- image: Optional[Image] = DEFAULT_IMAGE_3_10,
191
+ image: Optional[Image] = DEFAULT_IMAGE,
192
192
  accumulate: Optional[Type[BaseModel]] = None,
193
193
  input_encoder: Optional[str] = "cloudpickle",
194
194
  output_encoder: Optional[str] = "cloudpickle",