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.
- indexify/cli.py +92 -52
- indexify/executor/agent.py +99 -187
- indexify/executor/api_objects.py +2 -8
- indexify/executor/downloader.py +129 -90
- indexify/executor/executor_tasks.py +15 -30
- indexify/executor/function_executor/function_executor.py +32 -0
- indexify/executor/function_executor/function_executor_factory.py +26 -0
- indexify/executor/function_executor/function_executor_map.py +91 -0
- indexify/executor/function_executor/process_function_executor.py +64 -0
- indexify/executor/function_executor/process_function_executor_factory.py +102 -0
- indexify/executor/function_worker.py +227 -184
- indexify/executor/runtime_probes.py +9 -8
- indexify/executor/task_fetcher.py +80 -0
- indexify/executor/task_reporter.py +18 -25
- indexify/executor/task_store.py +35 -16
- indexify/function_executor/function_executor_service.py +86 -0
- indexify/function_executor/handlers/run_function/function_inputs_loader.py +54 -0
- indexify/function_executor/handlers/run_function/handler.py +149 -0
- indexify/function_executor/handlers/run_function/request_validator.py +24 -0
- indexify/function_executor/handlers/run_function/response_helper.py +98 -0
- indexify/function_executor/initialize_request_validator.py +22 -0
- indexify/function_executor/proto/configuration.py +13 -0
- indexify/function_executor/proto/function_executor.proto +70 -0
- indexify/function_executor/proto/function_executor_pb2.py +53 -0
- indexify/function_executor/proto/function_executor_pb2.pyi +125 -0
- indexify/function_executor/proto/function_executor_pb2_grpc.py +163 -0
- indexify/function_executor/proto/message_validator.py +38 -0
- indexify/function_executor/server.py +31 -0
- indexify/functions_sdk/data_objects.py +0 -9
- indexify/functions_sdk/graph.py +10 -11
- indexify/functions_sdk/graph_definition.py +2 -2
- indexify/functions_sdk/image.py +35 -30
- indexify/functions_sdk/indexify_functions.py +5 -5
- indexify/http_client.py +15 -23
- indexify/logging.py +32 -0
- {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/METADATA +3 -1
- indexify-0.2.42.dist-info/RECORD +53 -0
- indexify/executor/indexify_executor.py +0 -32
- indexify-0.2.40.dist-info/RECORD +0 -34
- {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/LICENSE.txt +0 -0
- {indexify-0.2.40.dist-info → indexify-0.2.42.dist-info}/WHEEL +0 -0
- {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
|
indexify/functions_sdk/graph.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
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
|
269
|
-
else
|
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)
|
indexify/functions_sdk/image.py
CHANGED
@@ -1,34 +1,29 @@
|
|
1
|
-
|
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
|
20
|
+
def __init__(self):
|
27
21
|
self._image_name = None
|
28
22
|
self._tag = "latest"
|
29
|
-
self._base_image =
|
30
|
-
self._python_version =
|
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
|
-
|
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
|
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] =
|
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] =
|
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] =
|
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] =
|
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",
|