indexify 0.3.4__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {indexify-0.3.4 → indexify-0.3.6}/PKG-INFO +3 -4
- {indexify-0.3.4 → indexify-0.3.6}/pyproject.toml +5 -8
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/cli/cli.py +6 -4
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/downloader.py +2 -3
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/executor.py +8 -7
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/function_executor.py +3 -4
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/health_checker.py +4 -4
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/invocation_state_client.py +3 -4
- indexify-0.3.4/src/indexify/function_executor/proto/configuration.py → indexify-0.3.6/src/indexify/executor/function_executor/server/client_configuration.py +7 -11
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/server/subprocess_function_executor_server.py +1 -2
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py +0 -5
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/single_task_runner.py +2 -4
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/task_input.py +1 -1
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/task_output.py +1 -1
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/task_fetcher.py +5 -5
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/task_reporter.py +3 -4
- indexify-0.3.4/src/indexify/function_executor/README.md +0 -18
- indexify-0.3.4/src/indexify/function_executor/handlers/run_function/function_inputs_loader.py +0 -53
- indexify-0.3.4/src/indexify/function_executor/handlers/run_function/handler.py +0 -126
- indexify-0.3.4/src/indexify/function_executor/handlers/run_function/request_validator.py +0 -26
- indexify-0.3.4/src/indexify/function_executor/handlers/run_function/response_helper.py +0 -96
- indexify-0.3.4/src/indexify/function_executor/initialize_request_validator.py +0 -21
- indexify-0.3.4/src/indexify/function_executor/invocation_state/invocation_state_proxy_server.py +0 -170
- indexify-0.3.4/src/indexify/function_executor/invocation_state/proxied_invocation_state.py +0 -22
- indexify-0.3.4/src/indexify/function_executor/invocation_state/response_validator.py +0 -29
- indexify-0.3.4/src/indexify/function_executor/main.py +0 -50
- indexify-0.3.4/src/indexify/function_executor/proto/function_executor.proto +0 -130
- indexify-0.3.4/src/indexify/function_executor/proto/function_executor_pb2.py +0 -69
- indexify-0.3.4/src/indexify/function_executor/proto/function_executor_pb2.pyi +0 -225
- indexify-0.3.4/src/indexify/function_executor/proto/function_executor_pb2_grpc.py +0 -260
- indexify-0.3.4/src/indexify/function_executor/proto/message_validator.py +0 -38
- indexify-0.3.4/src/indexify/function_executor/server.py +0 -29
- indexify-0.3.4/src/indexify/function_executor/service.py +0 -133
- indexify-0.3.4/src/indexify/utils/README.md +0 -3
- indexify-0.3.4/src/indexify/utils/http_client.py +0 -88
- indexify-0.3.4/src/indexify/utils/logging.py +0 -66
- {indexify-0.3.4 → indexify-0.3.6}/README.md +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/README.md +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/api_objects.py +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/function_executor_state.py +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/server/function_executor_server.py +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/server/function_executor_server_factory.py +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/runtime_probes.py +0 -0
- {indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/task_runner.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: indexify
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.6
|
4
4
|
Summary: Open Source Indexify components and helper tools
|
5
5
|
Home-page: https://github.com/tensorlakeai/indexify
|
6
6
|
License: Apache 2.0
|
@@ -14,8 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
17
|
-
Requires-Dist: grpcio (==1.
|
18
|
-
Requires-Dist: grpcio-tools (==1.68.1)
|
17
|
+
Requires-Dist: grpcio (==1.70.0)
|
19
18
|
Requires-Dist: httpx-sse (>=0.4.0,<0.5.0)
|
20
19
|
Requires-Dist: httpx[http2] (>=0.27,<0.28)
|
21
20
|
Requires-Dist: nanoid (>=2.0.0,<3.0.0)
|
@@ -23,7 +22,7 @@ Requires-Dist: pydantic (==2.10.4)
|
|
23
22
|
Requires-Dist: pyyaml (>=6,<7)
|
24
23
|
Requires-Dist: rich (>=13.9.2,<14.0.0)
|
25
24
|
Requires-Dist: structlog (>=24.4.0,<25.0.0)
|
26
|
-
Requires-Dist: tensorlake (>=0.1.
|
25
|
+
Requires-Dist: tensorlake (>=0.1.13)
|
27
26
|
Requires-Dist: typer (>=0.12,<0.13)
|
28
27
|
Project-URL: Repository, https://github.com/tensorlakeai/indexify
|
29
28
|
Description-Content-Type: text/markdown
|
@@ -1,7 +1,7 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "indexify"
|
3
3
|
# Incremented if any of the components provided in this packages are updated.
|
4
|
-
version = "0.3.
|
4
|
+
version = "0.3.6"
|
5
5
|
description = "Open Source Indexify components and helper tools"
|
6
6
|
authors = ["Tensorlake Inc. <support@tensorlake.ai>"]
|
7
7
|
license = "Apache 2.0"
|
@@ -11,7 +11,6 @@ repository = "https://github.com/tensorlakeai/indexify"
|
|
11
11
|
|
12
12
|
[tool.poetry.scripts]
|
13
13
|
indexify-cli = "indexify.cli.cli:app"
|
14
|
-
function-executor = "indexify.function_executor.main:main"
|
15
14
|
|
16
15
|
[tool.poetry.dependencies]
|
17
16
|
# Common dependencies
|
@@ -19,15 +18,13 @@ python = "^3.9"
|
|
19
18
|
structlog = "^24.4.0"
|
20
19
|
pyyaml = "^6"
|
21
20
|
httpx = { version = "^0.27", extras = ["http2"] }
|
22
|
-
grpcio = "1.
|
23
|
-
|
24
|
-
# Function Executor only
|
25
|
-
grpcio-tools = "1.68.1"
|
26
|
-
tensorlake = ">=0.1.9"
|
21
|
+
grpcio = "1.70.0"
|
27
22
|
|
28
23
|
# Executor only
|
29
24
|
pydantic = "2.10.4"
|
30
25
|
httpx-sse = "^0.4.0"
|
26
|
+
# Adds function-executor binary and utils lib.
|
27
|
+
tensorlake = ">=0.1.13"
|
31
28
|
|
32
29
|
# CLI only
|
33
30
|
rich = "^13.9.2"
|
@@ -36,7 +33,7 @@ nanoid = "^2.0.0"
|
|
36
33
|
|
37
34
|
[tool.poetry.group.dev.dependencies]
|
38
35
|
black = "^24.10.0"
|
39
|
-
pylint = "^
|
36
|
+
pylint = "^3.3.0"
|
40
37
|
parameterized = "^0.9.0"
|
41
38
|
|
42
39
|
[build-system]
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from
|
1
|
+
from tensorlake.utils.logging import (
|
2
2
|
configure_development_mode_logging,
|
3
3
|
configure_logging_early,
|
4
4
|
configure_production_mode_logging,
|
@@ -219,12 +219,13 @@ def executor(
|
|
219
219
|
)
|
220
220
|
|
221
221
|
id = nanoid.generate()
|
222
|
+
executor_version = version("indexify")
|
222
223
|
logger.info(
|
223
224
|
"starting executor",
|
224
225
|
server_addr=server_addr,
|
225
226
|
config_path=config_path,
|
226
227
|
executor_id=id,
|
227
|
-
executor_version=
|
228
|
+
executor_version=executor_version,
|
228
229
|
executor_cache=executor_cache,
|
229
230
|
ports=ports,
|
230
231
|
functions=function_uris,
|
@@ -247,7 +248,8 @@ def executor(
|
|
247
248
|
exit(1)
|
248
249
|
|
249
250
|
Executor(
|
250
|
-
id,
|
251
|
+
id=id,
|
252
|
+
version=executor_version,
|
251
253
|
server_addr=server_addr,
|
252
254
|
config_path=config_path,
|
253
255
|
code_path=executor_cache,
|
@@ -290,7 +292,7 @@ def _parse_function_uris(uri_strs: Optional[List[str]]) -> Optional[List[Functio
|
|
290
292
|
|
291
293
|
def _create_image(image: Image, python_sdk_path):
|
292
294
|
console.print(
|
293
|
-
Text("Creating
|
295
|
+
Text("Creating image for ", style="cyan"),
|
294
296
|
Text(f"`{image._image_name}`", style="cyan bold"),
|
295
297
|
)
|
296
298
|
_build_image(image=image, python_sdk_path=python_sdk_path)
|
@@ -4,9 +4,8 @@ from typing import Any, Optional
|
|
4
4
|
|
5
5
|
import httpx
|
6
6
|
import structlog
|
7
|
-
|
8
|
-
from
|
9
|
-
from indexify.utils.http_client import get_httpx_client
|
7
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import SerializedObject
|
8
|
+
from tensorlake.utils.http_client import get_httpx_client
|
10
9
|
|
11
10
|
from .api_objects import Task
|
12
11
|
|
@@ -4,9 +4,8 @@ from pathlib import Path
|
|
4
4
|
from typing import Any, List, Optional
|
5
5
|
|
6
6
|
import structlog
|
7
|
-
|
8
|
-
from
|
9
|
-
from indexify.utils.logging import suppress as suppress_logging
|
7
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import SerializedObject
|
8
|
+
from tensorlake.utils.logging import suppress as suppress_logging
|
10
9
|
|
11
10
|
from .api_objects import FunctionURI, Task
|
12
11
|
from .downloader import Downloader
|
@@ -21,7 +20,8 @@ from .task_runner import TaskInput, TaskOutput, TaskRunner
|
|
21
20
|
class Executor:
|
22
21
|
def __init__(
|
23
22
|
self,
|
24
|
-
|
23
|
+
id: str,
|
24
|
+
version: str,
|
25
25
|
code_path: Path,
|
26
26
|
function_allowlist: Optional[List[FunctionURI]],
|
27
27
|
function_executor_server_factory: FunctionExecutorServerFactory,
|
@@ -48,15 +48,16 @@ class Executor:
|
|
48
48
|
code_path=code_path, base_url=self._base_url, config_path=config_path
|
49
49
|
)
|
50
50
|
self._task_fetcher = TaskFetcher(
|
51
|
+
executor_id=id,
|
52
|
+
executor_version=version,
|
53
|
+
function_allowlist=function_allowlist,
|
51
54
|
protocol=protocol,
|
52
55
|
indexify_server_addr=self._server_addr,
|
53
|
-
executor_id=executor_id,
|
54
|
-
function_allowlist=function_allowlist,
|
55
56
|
config_path=config_path,
|
56
57
|
)
|
57
58
|
self._task_reporter = TaskReporter(
|
58
59
|
base_url=self._base_url,
|
59
|
-
executor_id=
|
60
|
+
executor_id=id,
|
60
61
|
config_path=self._config_path,
|
61
62
|
)
|
62
63
|
|
{indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/function_executor.py
RENAMED
@@ -2,15 +2,14 @@ import asyncio
|
|
2
2
|
from typing import Any, Optional
|
3
3
|
|
4
4
|
import grpc
|
5
|
-
|
6
|
-
from indexify.function_executor.proto.function_executor_pb2 import (
|
5
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import (
|
7
6
|
InitializeRequest,
|
8
7
|
InitializeResponse,
|
9
8
|
)
|
10
|
-
from
|
9
|
+
from tensorlake.function_executor.proto.function_executor_pb2_grpc import (
|
11
10
|
FunctionExecutorStub,
|
12
11
|
)
|
13
|
-
from
|
12
|
+
from tensorlake.utils.http_client import get_httpx_client
|
14
13
|
|
15
14
|
from .health_checker import HealthChecker
|
16
15
|
from .invocation_state_client import InvocationStateClient
|
@@ -3,16 +3,16 @@ from collections.abc import Awaitable, Callable
|
|
3
3
|
from typing import Any, Optional
|
4
4
|
|
5
5
|
from grpc.aio import AioRpcError
|
6
|
-
|
7
|
-
from indexify.function_executor.proto.configuration import HEALTH_CHECK_TIMEOUT_SEC
|
8
|
-
from indexify.function_executor.proto.function_executor_pb2 import (
|
6
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import (
|
9
7
|
HealthCheckRequest,
|
10
8
|
HealthCheckResponse,
|
11
9
|
)
|
12
|
-
from
|
10
|
+
from tensorlake.function_executor.proto.function_executor_pb2_grpc import (
|
13
11
|
FunctionExecutorStub,
|
14
12
|
)
|
15
13
|
|
14
|
+
from .server.client_configuration import HEALTH_CHECK_TIMEOUT_SEC
|
15
|
+
|
16
16
|
HEALTH_CHECK_POLL_PERIOD_SEC = 10
|
17
17
|
|
18
18
|
|
{indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/invocation_state_client.py
RENAMED
@@ -3,18 +3,17 @@ from typing import Any, AsyncGenerator, Optional, Union
|
|
3
3
|
|
4
4
|
import grpc
|
5
5
|
import httpx
|
6
|
-
|
7
|
-
from indexify.function_executor.proto.function_executor_pb2 import (
|
6
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import (
|
8
7
|
GetInvocationStateResponse,
|
9
8
|
InvocationStateRequest,
|
10
9
|
InvocationStateResponse,
|
11
10
|
SerializedObject,
|
12
11
|
SetInvocationStateResponse,
|
13
12
|
)
|
14
|
-
from
|
13
|
+
from tensorlake.function_executor.proto.function_executor_pb2_grpc import (
|
15
14
|
FunctionExecutorStub,
|
16
15
|
)
|
17
|
-
from
|
16
|
+
from tensorlake.function_executor.proto.message_validator import MessageValidator
|
18
17
|
|
19
18
|
from ..downloader import serialized_object_from_http_response
|
20
19
|
|
@@ -4,21 +4,17 @@
|
|
4
4
|
# This is due to internal hard gRPC limits. When we want to increase the message sizes
|
5
5
|
# we'll have to implement chunking for large messages.
|
6
6
|
_MAX_GRPC_MESSAGE_LENGTH = -1
|
7
|
-
# Disable port reuse: fail if multiple Function Executor Servers attempt to bind to the
|
8
|
-
# same port. This happens when Indexify users misconfigure the Servers. Disabling the port
|
9
|
-
# reuse results in a clear error message on Server startup instead of obscure errors later
|
10
|
-
# while Indexify cluster is serving tasks.
|
11
|
-
# If we don't disable port reuse then a random Server gets the requests so wrong tasks get
|
12
|
-
# routed to wrong servers.
|
13
|
-
_REUSE_SERVER_PORT = 0
|
14
7
|
|
15
|
-
|
8
|
+
# Optimize the channels for low latency connection establishement as we are running on the same host.
|
9
|
+
_RECONNECT_BACKOFF_MS = 100
|
10
|
+
|
11
|
+
GRPC_CHANNEL_OPTIONS = [
|
16
12
|
("grpc.max_receive_message_length", _MAX_GRPC_MESSAGE_LENGTH),
|
17
13
|
("grpc.max_send_message_length", _MAX_GRPC_MESSAGE_LENGTH),
|
18
|
-
("grpc.
|
14
|
+
("grpc.min_reconnect_backoff_ms", _RECONNECT_BACKOFF_MS),
|
15
|
+
("grpc.max_reconnect_backoff_ms", _RECONNECT_BACKOFF_MS),
|
16
|
+
("grpc.initial_reconnect_backoff_ms", _RECONNECT_BACKOFF_MS),
|
19
17
|
]
|
20
18
|
|
21
|
-
GRPC_CHANNEL_OPTIONS = GRPC_SERVER_OPTIONS
|
22
|
-
|
23
19
|
# If a health check takes more than this duration then the server is considered unhealthy.
|
24
20
|
HEALTH_CHECK_TIMEOUT_SEC = 5
|
@@ -22,11 +22,6 @@ class SubprocessFunctionExecutorServerFactory(FunctionExecutorServerFactory):
|
|
22
22
|
async def create(
|
23
23
|
self, config: FunctionExecutorServerConfiguration, logger: Any
|
24
24
|
) -> SubprocessFunctionExecutorServer:
|
25
|
-
if config.image_uri is not None:
|
26
|
-
raise ValueError(
|
27
|
-
"SubprocessFunctionExecutorServerFactory doesn't support container images"
|
28
|
-
)
|
29
|
-
|
30
25
|
logger = logger.bind(module=__name__)
|
31
26
|
port: Optional[int] = None
|
32
27
|
|
{indexify-0.3.4 → indexify-0.3.6}/src/indexify/executor/function_executor/single_task_runner.py
RENAMED
@@ -2,14 +2,12 @@ from collections.abc import Awaitable, Callable
|
|
2
2
|
from typing import Any, Optional
|
3
3
|
|
4
4
|
import grpc
|
5
|
-
from
|
6
|
-
|
7
|
-
from indexify.function_executor.proto.function_executor_pb2 import (
|
5
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import (
|
8
6
|
InitializeRequest,
|
9
7
|
RunTaskRequest,
|
10
8
|
RunTaskResponse,
|
11
9
|
)
|
12
|
-
from
|
10
|
+
from tensorlake.function_executor.proto.function_executor_pb2_grpc import (
|
13
11
|
FunctionExecutorStub,
|
14
12
|
)
|
15
13
|
|
@@ -4,8 +4,7 @@ from typing import AsyncGenerator, List, Optional
|
|
4
4
|
|
5
5
|
import structlog
|
6
6
|
from httpx_sse import aconnect_sse
|
7
|
-
|
8
|
-
from indexify.utils.http_client import get_httpx_client
|
7
|
+
from tensorlake.utils.http_client import get_httpx_client
|
9
8
|
|
10
9
|
from .api_objects import ExecutorMetadata, FunctionURI, Task
|
11
10
|
from .runtime_probes import ProbeInfo, RuntimeProbes
|
@@ -16,10 +15,11 @@ class TaskFetcher:
|
|
16
15
|
|
17
16
|
def __init__(
|
18
17
|
self,
|
19
|
-
protocol: str,
|
20
|
-
indexify_server_addr: str,
|
21
18
|
executor_id: str,
|
19
|
+
executor_version: str,
|
22
20
|
function_allowlist: Optional[List[FunctionURI]],
|
21
|
+
protocol: str,
|
22
|
+
indexify_server_addr: str,
|
23
23
|
config_path: Optional[str] = None,
|
24
24
|
):
|
25
25
|
self._protocol: str = protocol
|
@@ -30,7 +30,7 @@ class TaskFetcher:
|
|
30
30
|
probe_info: ProbeInfo = RuntimeProbes().probe()
|
31
31
|
self._executor_metadata: ExecutorMetadata = ExecutorMetadata(
|
32
32
|
id=executor_id,
|
33
|
-
executor_version=
|
33
|
+
executor_version=executor_version,
|
34
34
|
addr="",
|
35
35
|
function_allowlist=function_allowlist,
|
36
36
|
labels=probe_info.labels,
|
@@ -3,11 +3,10 @@ from typing import Any, List, Optional, Tuple
|
|
3
3
|
|
4
4
|
import nanoid
|
5
5
|
from httpx import Timeout
|
6
|
+
from tensorlake.function_executor.proto.function_executor_pb2 import FunctionOutput
|
7
|
+
from tensorlake.utils.http_client import get_httpx_client
|
6
8
|
|
7
|
-
from
|
8
|
-
from indexify.utils.http_client import get_httpx_client
|
9
|
-
|
10
|
-
from .api_objects import RouterOutput, Task, TaskResult
|
9
|
+
from .api_objects import RouterOutput, TaskResult
|
11
10
|
from .task_runner import TaskOutput
|
12
11
|
|
13
12
|
|
@@ -1,18 +0,0 @@
|
|
1
|
-
## Overview
|
2
|
-
|
3
|
-
Function Executor is a process with an API that allows to load and run a customer Function in Indexify.
|
4
|
-
Each function run is a task. The tasks can be executed concurrently. The API client controls
|
5
|
-
the desired concurrency. Killing the process allows to free all the resources that a loaded customer
|
6
|
-
functon is using. This is helpful because the SDK doesn't provide any callbacks to customer code to free
|
7
|
-
resources it's using. Even if there was such callback customer code still might misbehave.
|
8
|
-
|
9
|
-
## Deployment
|
10
|
-
|
11
|
-
A Function Executor is created and destroyed by another component called Executor. It also calls the
|
12
|
-
Function Executor APIs. The server is not expected to be deployed or managed manually by Indexify users
|
13
|
-
as it's a low level component.
|
14
|
-
|
15
|
-
## Threat model
|
16
|
-
|
17
|
-
Customer code is assumed to be not trusted. Function Executor must not obtain any credentials that grant
|
18
|
-
access to resources not owned by the customer who owns the function.
|
indexify-0.3.4/src/indexify/function_executor/handlers/run_function/function_inputs_loader.py
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
from tensorlake.functions_sdk.data_objects import TensorlakeData
|
4
|
-
from tensorlake.functions_sdk.object_serializer import get_serializer
|
5
|
-
|
6
|
-
from ...proto.function_executor_pb2 import RunTaskRequest, SerializedObject
|
7
|
-
|
8
|
-
|
9
|
-
class FunctionInputs:
|
10
|
-
def __init__(
|
11
|
-
self, input: TensorlakeData, init_value: Optional[TensorlakeData] = None
|
12
|
-
):
|
13
|
-
self.input = input
|
14
|
-
self.init_value = init_value
|
15
|
-
|
16
|
-
|
17
|
-
class FunctionInputsLoader:
|
18
|
-
def __init__(self, request: RunTaskRequest):
|
19
|
-
self._request = request
|
20
|
-
|
21
|
-
def load(self) -> FunctionInputs:
|
22
|
-
return FunctionInputs(
|
23
|
-
input=self._function_input(),
|
24
|
-
init_value=self._accumulator_input(),
|
25
|
-
)
|
26
|
-
|
27
|
-
def _function_input(self) -> TensorlakeData:
|
28
|
-
return _to_indexify_data(
|
29
|
-
self._request.graph_invocation_id, self._request.function_input
|
30
|
-
)
|
31
|
-
|
32
|
-
def _accumulator_input(self) -> Optional[TensorlakeData]:
|
33
|
-
return (
|
34
|
-
_to_indexify_data(
|
35
|
-
self._request.graph_invocation_id, self._request.function_init_value
|
36
|
-
)
|
37
|
-
if self._request.HasField("function_init_value")
|
38
|
-
else None
|
39
|
-
)
|
40
|
-
|
41
|
-
|
42
|
-
def _to_indexify_data(
|
43
|
-
input_id: str, serialized_object: SerializedObject
|
44
|
-
) -> TensorlakeData:
|
45
|
-
return TensorlakeData(
|
46
|
-
input_id=input_id,
|
47
|
-
payload=(
|
48
|
-
serialized_object.bytes
|
49
|
-
if serialized_object.HasField("bytes")
|
50
|
-
else serialized_object.string
|
51
|
-
),
|
52
|
-
encoder=get_serializer(serialized_object.content_type).encoding_type,
|
53
|
-
)
|
@@ -1,126 +0,0 @@
|
|
1
|
-
import io
|
2
|
-
import sys
|
3
|
-
import traceback
|
4
|
-
from contextlib import redirect_stderr, redirect_stdout
|
5
|
-
from typing import Any, Union
|
6
|
-
|
7
|
-
from tensorlake.functions_sdk.functions import (
|
8
|
-
FunctionCallResult,
|
9
|
-
GraphInvocationContext,
|
10
|
-
RouterCallResult,
|
11
|
-
TensorlakeCompute,
|
12
|
-
TensorlakeFunctionWrapper,
|
13
|
-
TensorlakeRouter,
|
14
|
-
)
|
15
|
-
from tensorlake.functions_sdk.invocation_state.invocation_state import InvocationState
|
16
|
-
|
17
|
-
from ...proto.function_executor_pb2 import RunTaskRequest, RunTaskResponse
|
18
|
-
from .function_inputs_loader import FunctionInputs, FunctionInputsLoader
|
19
|
-
from .response_helper import ResponseHelper
|
20
|
-
|
21
|
-
|
22
|
-
class Handler:
|
23
|
-
def __init__(
|
24
|
-
self,
|
25
|
-
request: RunTaskRequest,
|
26
|
-
graph_name: str,
|
27
|
-
graph_version: str,
|
28
|
-
function_name: str,
|
29
|
-
function: Union[TensorlakeCompute, TensorlakeCompute],
|
30
|
-
invocation_state: InvocationState,
|
31
|
-
logger: Any,
|
32
|
-
):
|
33
|
-
self._function_name: str = function_name
|
34
|
-
self._logger = logger.bind(
|
35
|
-
graph_invocation_id=request.graph_invocation_id,
|
36
|
-
task_id=request.task_id,
|
37
|
-
)
|
38
|
-
self._input_loader = FunctionInputsLoader(request)
|
39
|
-
self._response_helper = ResponseHelper(task_id=request.task_id)
|
40
|
-
# TODO: use files for stdout, stderr capturing. This puts a natural and thus reasonable
|
41
|
-
# rate limit on the rate of writes and allows to not consume expensive memory for function logs.
|
42
|
-
self._func_stdout: io.StringIO = io.StringIO()
|
43
|
-
self._func_stderr: io.StringIO = io.StringIO()
|
44
|
-
|
45
|
-
self._function_wrapper: TensorlakeFunctionWrapper = TensorlakeFunctionWrapper(
|
46
|
-
indexify_function=function,
|
47
|
-
context=GraphInvocationContext(
|
48
|
-
invocation_id=request.graph_invocation_id,
|
49
|
-
graph_name=graph_name,
|
50
|
-
graph_version=graph_version,
|
51
|
-
invocation_state=invocation_state,
|
52
|
-
),
|
53
|
-
)
|
54
|
-
|
55
|
-
def run(self) -> RunTaskResponse:
|
56
|
-
"""Runs the task.
|
57
|
-
|
58
|
-
Raises an exception if our own code failed, customer function failure doesn't result in any exception.
|
59
|
-
Details of customer function failure are returned in the response.
|
60
|
-
"""
|
61
|
-
self._logger.info("running function")
|
62
|
-
inputs: FunctionInputs = self._input_loader.load()
|
63
|
-
self._flush_logs()
|
64
|
-
return self._run_func_safe_and_captured(inputs)
|
65
|
-
|
66
|
-
def _run_func_safe_and_captured(self, inputs: FunctionInputs) -> RunTaskResponse:
|
67
|
-
"""Runs the customer function while capturing what happened in it.
|
68
|
-
|
69
|
-
Function stdout and stderr are captured so they don't get into Function Executor process stdout
|
70
|
-
and stderr. Never throws an Exception. Caller can determine if the function succeeded
|
71
|
-
using the response.
|
72
|
-
"""
|
73
|
-
try:
|
74
|
-
with redirect_stdout(self._func_stdout), redirect_stderr(self._func_stderr):
|
75
|
-
return self._run_func(inputs)
|
76
|
-
except Exception:
|
77
|
-
return self._response_helper.failure_response(
|
78
|
-
message=traceback.format_exc(),
|
79
|
-
stdout=self._func_stdout.getvalue(),
|
80
|
-
stderr=self._func_stderr.getvalue(),
|
81
|
-
)
|
82
|
-
|
83
|
-
def _run_func(self, inputs: FunctionInputs) -> RunTaskResponse:
|
84
|
-
if _is_router(self._function_wrapper):
|
85
|
-
result: RouterCallResult = self._function_wrapper.invoke_router(
|
86
|
-
self._function_name, inputs.input
|
87
|
-
)
|
88
|
-
return self._response_helper.router_response(
|
89
|
-
result=result,
|
90
|
-
stdout=self._func_stdout.getvalue(),
|
91
|
-
stderr=self._func_stderr.getvalue(),
|
92
|
-
)
|
93
|
-
else:
|
94
|
-
result: FunctionCallResult = self._function_wrapper.invoke_fn_ser(
|
95
|
-
self._function_name, inputs.input, inputs.init_value
|
96
|
-
)
|
97
|
-
return self._response_helper.function_response(
|
98
|
-
result=result,
|
99
|
-
is_reducer=_func_is_reducer(self._function_wrapper),
|
100
|
-
stdout=self._func_stdout.getvalue(),
|
101
|
-
stderr=self._func_stderr.getvalue(),
|
102
|
-
)
|
103
|
-
|
104
|
-
def _flush_logs(self) -> None:
|
105
|
-
# Flush any logs buffered in memory before running the function with stdout, stderr capture.
|
106
|
-
# Otherwise our logs logged before this point will end up in the function's stdout.
|
107
|
-
# structlog.PrintLogger uses print function. This is why flushing with print works.
|
108
|
-
print("", flush=True)
|
109
|
-
sys.stdout.flush()
|
110
|
-
sys.stderr.flush()
|
111
|
-
|
112
|
-
|
113
|
-
def _is_router(func_wrapper: TensorlakeFunctionWrapper) -> bool:
|
114
|
-
"""Determines if the function is a router.
|
115
|
-
|
116
|
-
A function is a router if it is an instance of TensorlakeRouter or if it is an TensorlakeRouter class.
|
117
|
-
"""
|
118
|
-
return str(
|
119
|
-
type(func_wrapper.indexify_function)
|
120
|
-
) == "<class 'tensorlake.functions_sdk.functions.TensorlakeRouter'>" or isinstance(
|
121
|
-
func_wrapper.indexify_function, TensorlakeRouter
|
122
|
-
)
|
123
|
-
|
124
|
-
|
125
|
-
def _func_is_reducer(func_wrapper: TensorlakeFunctionWrapper) -> bool:
|
126
|
-
return func_wrapper.indexify_function.accumulate is not None
|
@@ -1,26 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
|
3
|
-
from ...proto.function_executor_pb2 import RunTaskRequest
|
4
|
-
from ...proto.message_validator import MessageValidator
|
5
|
-
|
6
|
-
|
7
|
-
class RequestValidator:
|
8
|
-
def __init__(self, request: RunTaskRequest):
|
9
|
-
self._request = request
|
10
|
-
self._message_validator = MessageValidator(request)
|
11
|
-
|
12
|
-
def check(self):
|
13
|
-
"""Validates the request.
|
14
|
-
|
15
|
-
Raises: ValueError: If the request is invalid.
|
16
|
-
"""
|
17
|
-
(
|
18
|
-
self._message_validator.required_field("namespace")
|
19
|
-
.required_field("graph_name")
|
20
|
-
.required_field("graph_version")
|
21
|
-
.required_field("function_name")
|
22
|
-
.required_field("graph_invocation_id")
|
23
|
-
.required_field("task_id")
|
24
|
-
.required_serialized_object("function_input")
|
25
|
-
.optional_serialized_object("function_init_value")
|
26
|
-
)
|