indexify 0.2.39__tar.gz → 0.2.41__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.2.39 → indexify-0.2.41}/PKG-INFO +3 -1
- {indexify-0.2.39 → indexify-0.2.41}/indexify/cli.py +92 -52
- indexify-0.2.41/indexify/executor/agent.py +262 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/executor/api_objects.py +2 -8
- indexify-0.2.41/indexify/executor/downloader.py +165 -0
- indexify-0.2.41/indexify/executor/executor_tasks.py +58 -0
- indexify-0.2.41/indexify/executor/function_executor/function_executor.py +32 -0
- indexify-0.2.41/indexify/executor/function_executor/function_executor_factory.py +26 -0
- indexify-0.2.41/indexify/executor/function_executor/function_executor_map.py +91 -0
- indexify-0.2.41/indexify/executor/function_executor/process_function_executor.py +64 -0
- indexify-0.2.41/indexify/executor/function_executor/process_function_executor_factory.py +102 -0
- indexify-0.2.41/indexify/executor/function_worker.py +255 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/executor/runtime_probes.py +9 -8
- indexify-0.2.41/indexify/executor/task_fetcher.py +80 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/executor/task_reporter.py +18 -25
- {indexify-0.2.39 → indexify-0.2.41}/indexify/executor/task_store.py +35 -16
- indexify-0.2.41/indexify/function_executor/function_executor_service.py +86 -0
- indexify-0.2.41/indexify/function_executor/handlers/run_function/function_inputs_loader.py +54 -0
- indexify-0.2.41/indexify/function_executor/handlers/run_function/handler.py +149 -0
- indexify-0.2.41/indexify/function_executor/handlers/run_function/request_validator.py +24 -0
- indexify-0.2.41/indexify/function_executor/handlers/run_function/response_helper.py +98 -0
- indexify-0.2.41/indexify/function_executor/initialize_request_validator.py +22 -0
- indexify-0.2.41/indexify/function_executor/proto/configuration.py +13 -0
- indexify-0.2.41/indexify/function_executor/proto/function_executor.proto +70 -0
- indexify-0.2.41/indexify/function_executor/proto/function_executor_pb2.py +53 -0
- indexify-0.2.41/indexify/function_executor/proto/function_executor_pb2.pyi +125 -0
- indexify-0.2.41/indexify/function_executor/proto/function_executor_pb2_grpc.py +163 -0
- indexify-0.2.41/indexify/function_executor/proto/message_validator.py +38 -0
- indexify-0.2.41/indexify/function_executor/server.py +31 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/data_objects.py +0 -9
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/graph.py +17 -10
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/graph_definition.py +3 -2
- indexify-0.2.41/indexify/functions_sdk/image.py +77 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/indexify_functions.py +5 -5
- {indexify-0.2.39 → indexify-0.2.41}/indexify/http_client.py +15 -23
- indexify-0.2.41/indexify/logging.py +32 -0
- {indexify-0.2.39 → indexify-0.2.41}/pyproject.toml +3 -1
- indexify-0.2.39/indexify/executor/agent.py +0 -350
- indexify-0.2.39/indexify/executor/downloader.py +0 -126
- indexify-0.2.39/indexify/executor/executor_tasks.py +0 -73
- indexify-0.2.39/indexify/executor/function_worker.py +0 -212
- indexify-0.2.39/indexify/executor/indexify_executor.py +0 -32
- indexify-0.2.39/indexify/functions_sdk/image.py +0 -72
- {indexify-0.2.39 → indexify-0.2.41}/LICENSE.txt +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/README.md +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/__init__.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/common_util.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/data_loaders/__init__.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/data_loaders/local_directory_loader.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/data_loaders/url_loader.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/error.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/graph_validation.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/local_cache.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/object_serializer.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/functions_sdk/pipeline.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/remote_graph.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/remote_pipeline.py +0 -0
- {indexify-0.2.39 → indexify-0.2.41}/indexify/settings.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: indexify
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.41
|
4
4
|
Summary: Python Client for Indexify
|
5
5
|
Home-page: https://github.com/tensorlakeai/indexify
|
6
6
|
License: Apache 2.0
|
@@ -16,6 +16,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
17
17
|
Requires-Dist: cloudpickle (>=3.1.0,<4.0.0)
|
18
18
|
Requires-Dist: docker (>=7.1.0,<8.0.0)
|
19
|
+
Requires-Dist: grpcio (==1.68.1)
|
20
|
+
Requires-Dist: grpcio-tools (==1.68.1)
|
19
21
|
Requires-Dist: httpx-sse (>=0.4.0,<0.5.0)
|
20
22
|
Requires-Dist: httpx[http2] (==0.27.2)
|
21
23
|
Requires-Dist: nanoid (>=2.0.0,<3.0.0)
|
@@ -1,3 +1,8 @@
|
|
1
|
+
from .logging import configure_logging_early, configure_production_logging
|
2
|
+
|
3
|
+
configure_logging_early()
|
4
|
+
|
5
|
+
|
1
6
|
import asyncio
|
2
7
|
import os
|
3
8
|
import shutil
|
@@ -18,12 +23,18 @@ from rich.text import Text
|
|
18
23
|
from rich.theme import Theme
|
19
24
|
|
20
25
|
from indexify.executor.agent import ExtractorAgent
|
21
|
-
from indexify.
|
26
|
+
from indexify.function_executor.function_executor_service import (
|
27
|
+
FunctionExecutorService,
|
28
|
+
)
|
29
|
+
from indexify.function_executor.server import Server as FunctionExecutorServer
|
22
30
|
from indexify.functions_sdk.image import (
|
23
|
-
|
24
|
-
|
31
|
+
LOCAL_PYTHON_VERSION,
|
32
|
+
GetDefaultPythonImage,
|
25
33
|
Image,
|
26
34
|
)
|
35
|
+
from indexify.http_client import IndexifyClient
|
36
|
+
|
37
|
+
logger = structlog.get_logger(module=__name__)
|
27
38
|
|
28
39
|
custom_theme = Theme(
|
29
40
|
{
|
@@ -34,10 +45,12 @@ custom_theme = Theme(
|
|
34
45
|
}
|
35
46
|
)
|
36
47
|
|
37
|
-
logging = structlog.get_logger(module=__name__)
|
38
48
|
console = Console(theme=custom_theme)
|
39
49
|
|
40
50
|
app = typer.Typer(pretty_exceptions_enable=False, no_args_is_help=True)
|
51
|
+
config_path_option: Optional[str] = typer.Option(
|
52
|
+
None, help="Path to the TLS configuration file"
|
53
|
+
)
|
41
54
|
|
42
55
|
|
43
56
|
@app.command(
|
@@ -149,12 +162,21 @@ def build_image(
|
|
149
162
|
|
150
163
|
|
151
164
|
@app.command(help="Build default image for indexify")
|
152
|
-
def build_default_image(
|
153
|
-
|
154
|
-
|
165
|
+
def build_default_image(
|
166
|
+
python_version: Optional[str] = typer.Option(
|
167
|
+
f"{sys.version_info.major}.{sys.version_info.minor}",
|
168
|
+
help="Python version to use in the base image",
|
169
|
+
)
|
170
|
+
):
|
171
|
+
image = GetDefaultPythonImage(python_version)
|
172
|
+
|
173
|
+
_build_image(image=image)
|
155
174
|
|
156
175
|
console.print(
|
157
|
-
Text(f"Built default indexify image", style="cyan"),
|
176
|
+
Text(f"Built default indexify image with hash {image.hash()}\n", style="cyan"),
|
177
|
+
Text(
|
178
|
+
f"Don't forget to update your executors to run this image!", style="yellow"
|
179
|
+
),
|
158
180
|
)
|
159
181
|
|
160
182
|
|
@@ -164,42 +186,32 @@ def executor(
|
|
164
186
|
dev: Annotated[
|
165
187
|
bool, typer.Option("--dev", "-d", help="Run the executor in development mode")
|
166
188
|
] = False,
|
167
|
-
|
168
|
-
int, typer.Option(help="number of worker processes for extraction")
|
169
|
-
] = 1,
|
170
|
-
config_path: Optional[str] = typer.Option(
|
171
|
-
None, help="Path to the TLS configuration file"
|
172
|
-
),
|
189
|
+
config_path: Optional[str] = config_path_option,
|
173
190
|
executor_cache: Optional[str] = typer.Option(
|
174
191
|
"~/.indexify/executor_cache", help="Path to the executor cache directory"
|
175
192
|
),
|
176
193
|
name_alias: Optional[str] = typer.Option(
|
177
|
-
None, help="
|
194
|
+
None, help="Image name override for the executor"
|
178
195
|
),
|
179
|
-
|
180
|
-
|
196
|
+
image_hash: Optional[str] = typer.Option(
|
197
|
+
None, help="Image hash override for the executor"
|
181
198
|
),
|
182
199
|
):
|
183
|
-
# configure structured logging
|
184
200
|
if not dev:
|
185
|
-
|
186
|
-
structlog.processors.dict_tracebacks,
|
187
|
-
structlog.processors.JSONRenderer(),
|
188
|
-
]
|
189
|
-
structlog.configure(processors=processors)
|
201
|
+
configure_production_logging()
|
190
202
|
|
191
203
|
id = nanoid.generate()
|
192
204
|
executor_version = version("indexify")
|
193
|
-
|
205
|
+
logger.info(
|
194
206
|
"executor started",
|
195
|
-
workers=workers,
|
196
207
|
server_addr=server_addr,
|
197
208
|
config_path=config_path,
|
198
209
|
executor_id=id,
|
199
210
|
executor_version=executor_version,
|
200
211
|
executor_cache=executor_cache,
|
201
212
|
name_alias=name_alias,
|
202
|
-
|
213
|
+
image_hash=image_hash,
|
214
|
+
dev_mode=dev,
|
203
215
|
)
|
204
216
|
|
205
217
|
from pathlib import Path
|
@@ -211,18 +223,47 @@ def executor(
|
|
211
223
|
|
212
224
|
agent = ExtractorAgent(
|
213
225
|
id,
|
214
|
-
num_workers=workers,
|
215
226
|
server_addr=server_addr,
|
216
227
|
config_path=config_path,
|
217
228
|
code_path=executor_cache,
|
218
229
|
name_alias=name_alias,
|
219
|
-
|
230
|
+
image_hash=image_hash,
|
231
|
+
development_mode=dev,
|
220
232
|
)
|
221
233
|
|
222
234
|
try:
|
223
235
|
asyncio.get_event_loop().run_until_complete(agent.run())
|
224
236
|
except asyncio.CancelledError:
|
225
|
-
|
237
|
+
logger.info("graceful shutdown")
|
238
|
+
|
239
|
+
|
240
|
+
@app.command(help="Runs a Function Executor server")
|
241
|
+
def function_executor(
|
242
|
+
function_executor_server_address: str = typer.Option(
|
243
|
+
help="Function Executor server address"
|
244
|
+
),
|
245
|
+
indexify_server_address: str = typer.Option(help="Indexify server address"),
|
246
|
+
dev: Annotated[
|
247
|
+
bool, typer.Option("--dev", "-d", help="Run the executor in development mode")
|
248
|
+
] = False,
|
249
|
+
config_path: Optional[str] = config_path_option,
|
250
|
+
):
|
251
|
+
if not dev:
|
252
|
+
configure_production_logging()
|
253
|
+
|
254
|
+
logger.info(
|
255
|
+
"starting function executor server",
|
256
|
+
function_executor_server_address=function_executor_server_address,
|
257
|
+
indexify_server_address=indexify_server_address,
|
258
|
+
config_path=config_path,
|
259
|
+
)
|
260
|
+
|
261
|
+
FunctionExecutorServer(
|
262
|
+
server_address=function_executor_server_address,
|
263
|
+
service=FunctionExecutorService(
|
264
|
+
indexify_server_address=indexify_server_address, config_path=config_path
|
265
|
+
),
|
266
|
+
).run()
|
226
267
|
|
227
268
|
|
228
269
|
def _create_image(image: Image, python_sdk_path):
|
@@ -234,6 +275,7 @@ def _create_image(image: Image, python_sdk_path):
|
|
234
275
|
|
235
276
|
|
236
277
|
def _build_image(image: Image, python_sdk_path: Optional[str] = None):
|
278
|
+
|
237
279
|
try:
|
238
280
|
import docker
|
239
281
|
|
@@ -246,24 +288,31 @@ def _build_image(image: Image, python_sdk_path: Optional[str] = None):
|
|
246
288
|
)
|
247
289
|
exit(-1)
|
248
290
|
|
249
|
-
|
250
|
-
FROM {image._base_image}
|
251
|
-
|
252
|
-
RUN
|
291
|
+
docker_contents = [
|
292
|
+
f"FROM {image._base_image}",
|
293
|
+
"RUN mkdir -p ~/.indexify",
|
294
|
+
"RUN touch ~/.indexify/image_name",
|
295
|
+
f"RUN echo {image._image_name} > ~/.indexify/image_name",
|
296
|
+
f"RUN echo {image.hash()} > ~/.indexify/image_hash",
|
297
|
+
"WORKDIR /app",
|
298
|
+
]
|
253
299
|
|
254
|
-
RUN
|
300
|
+
docker_contents.extend(["RUN " + i for i in image._run_strs])
|
255
301
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
302
|
+
if python_sdk_path is not None:
|
303
|
+
logging.info(
|
304
|
+
f"Building image {image._image_name} with local version of the SDK"
|
305
|
+
)
|
306
|
+
if not os.path.exists(python_sdk_path):
|
307
|
+
print(f"error: {python_sdk_path} does not exist")
|
308
|
+
os.exit(1)
|
309
|
+
docker_contents.append(f"COPY {python_sdk_path} /app/python-sdk")
|
310
|
+
docker_contents.append("RUN (cd /app/python-sdk && pip install .)")
|
311
|
+
else:
|
312
|
+
docker_contents.append(f"RUN pip install indexify=={image._sdk_version}")
|
261
313
|
|
262
|
-
|
314
|
+
docker_file = "\n".join(docker_contents)
|
263
315
|
|
264
|
-
docker_file += "\n".join(run_strs)
|
265
|
-
print(os.getcwd())
|
266
|
-
import docker
|
267
316
|
import docker.api.build
|
268
317
|
|
269
318
|
docker.api.build.process_dockerfile = lambda dockerfile, path: (
|
@@ -271,15 +320,6 @@ WORKDIR /app
|
|
271
320
|
dockerfile,
|
272
321
|
)
|
273
322
|
|
274
|
-
if python_sdk_path is not None:
|
275
|
-
if not os.path.exists(python_sdk_path):
|
276
|
-
print(f"error: {python_sdk_path} does not exist")
|
277
|
-
os.exit(1)
|
278
|
-
docker_file += f"\nCOPY {python_sdk_path} /app/python-sdk"
|
279
|
-
docker_file += f"\nRUN (cd /app/python-sdk && pip install .)"
|
280
|
-
else:
|
281
|
-
docker_file += f"\nRUN pip install indexify"
|
282
|
-
|
283
323
|
console.print("Creating image using Dockerfile contents:", style="cyan bold")
|
284
324
|
print(f"{docker_file}")
|
285
325
|
|
@@ -0,0 +1,262 @@
|
|
1
|
+
import asyncio
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Dict, List, Optional
|
4
|
+
|
5
|
+
import structlog
|
6
|
+
|
7
|
+
from .downloader import Downloader
|
8
|
+
from .executor_tasks import DownloadGraphTask, DownloadInputsTask, RunTask
|
9
|
+
from .function_executor.process_function_executor_factory import (
|
10
|
+
ProcessFunctionExecutorFactory,
|
11
|
+
)
|
12
|
+
from .function_worker import (
|
13
|
+
FunctionWorker,
|
14
|
+
FunctionWorkerInput,
|
15
|
+
FunctionWorkerOutput,
|
16
|
+
)
|
17
|
+
from .task_fetcher import TaskFetcher
|
18
|
+
from .task_reporter import TaskReporter
|
19
|
+
from .task_store import CompletedTask, TaskStore
|
20
|
+
|
21
|
+
logger = structlog.get_logger(module=__name__)
|
22
|
+
|
23
|
+
|
24
|
+
class ExtractorAgent:
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
executor_id: str,
|
28
|
+
code_path: Path,
|
29
|
+
server_addr: str = "localhost:8900",
|
30
|
+
development_mode: bool = False,
|
31
|
+
config_path: Optional[str] = None,
|
32
|
+
name_alias: Optional[str] = None,
|
33
|
+
image_hash: Optional[str] = None,
|
34
|
+
):
|
35
|
+
self._config_path = config_path
|
36
|
+
protocol: str = "http"
|
37
|
+
if config_path:
|
38
|
+
logger.info("running the extractor with TLS enabled")
|
39
|
+
protocol = "https"
|
40
|
+
|
41
|
+
self._task_store: TaskStore = TaskStore()
|
42
|
+
self._function_worker = FunctionWorker(
|
43
|
+
function_executor_factory=ProcessFunctionExecutorFactory(
|
44
|
+
indexify_server_address=server_addr,
|
45
|
+
development_mode=development_mode,
|
46
|
+
config_path=config_path,
|
47
|
+
)
|
48
|
+
)
|
49
|
+
self._has_registered = False
|
50
|
+
self._server_addr = server_addr
|
51
|
+
self._base_url = f"{protocol}://{self._server_addr}"
|
52
|
+
self._code_path = code_path
|
53
|
+
self._downloader = Downloader(
|
54
|
+
code_path=code_path, base_url=self._base_url, config_path=config_path
|
55
|
+
)
|
56
|
+
self._task_fetcher = TaskFetcher(
|
57
|
+
protocol=protocol,
|
58
|
+
indexify_server_addr=self._server_addr,
|
59
|
+
executor_id=executor_id,
|
60
|
+
name_alias=name_alias,
|
61
|
+
image_hash=image_hash,
|
62
|
+
config_path=config_path,
|
63
|
+
)
|
64
|
+
self._task_reporter = TaskReporter(
|
65
|
+
base_url=self._base_url,
|
66
|
+
executor_id=executor_id,
|
67
|
+
config_path=self._config_path,
|
68
|
+
)
|
69
|
+
|
70
|
+
async def task_completion_reporter(self):
|
71
|
+
logger.info("starting task completion reporter")
|
72
|
+
# We should copy only the keys and not the values
|
73
|
+
while True:
|
74
|
+
outcomes = await self._task_store.task_outcomes()
|
75
|
+
for task_outcome in outcomes:
|
76
|
+
logger.info(
|
77
|
+
"reporting_task_outcome",
|
78
|
+
task_id=task_outcome.task.id,
|
79
|
+
fn_name=task_outcome.task.compute_fn,
|
80
|
+
num_outputs=(
|
81
|
+
len(task_outcome.function_output.outputs)
|
82
|
+
if task_outcome.function_output is not None
|
83
|
+
else 0
|
84
|
+
),
|
85
|
+
router_output=task_outcome.router_output,
|
86
|
+
outcome=task_outcome.task_outcome,
|
87
|
+
retries=task_outcome.reporting_retries,
|
88
|
+
)
|
89
|
+
|
90
|
+
try:
|
91
|
+
# Send task outcome to the server
|
92
|
+
self._task_reporter.report_task_outcome(completed_task=task_outcome)
|
93
|
+
except Exception as e:
|
94
|
+
# The connection was dropped in the middle of the reporting, process, retry
|
95
|
+
logger.error(
|
96
|
+
"failed_to_report_task",
|
97
|
+
task_id=task_outcome.task.id,
|
98
|
+
exc_info=e,
|
99
|
+
retries=task_outcome.reporting_retries,
|
100
|
+
)
|
101
|
+
task_outcome.reporting_retries += 1
|
102
|
+
await asyncio.sleep(5)
|
103
|
+
continue
|
104
|
+
|
105
|
+
self._task_store.mark_reported(task_id=task_outcome.task.id)
|
106
|
+
|
107
|
+
async def task_launcher(self):
|
108
|
+
async_tasks: List[asyncio.Task] = [
|
109
|
+
asyncio.create_task(
|
110
|
+
self._task_store.get_runnable_tasks(), name="get_runnable_tasks"
|
111
|
+
)
|
112
|
+
]
|
113
|
+
|
114
|
+
while True:
|
115
|
+
done, pending = await asyncio.wait(
|
116
|
+
async_tasks, return_when=asyncio.FIRST_COMPLETED
|
117
|
+
)
|
118
|
+
|
119
|
+
async_tasks: List[asyncio.Task] = list(pending)
|
120
|
+
for async_task in done:
|
121
|
+
if async_task.get_name() == "get_runnable_tasks":
|
122
|
+
if async_task.exception():
|
123
|
+
logger.error(
|
124
|
+
"task_launcher_error, failed to get runnable tasks",
|
125
|
+
exc_info=async_task.exception(),
|
126
|
+
)
|
127
|
+
continue
|
128
|
+
result: Dict[str, Task] = await async_task
|
129
|
+
task: Task
|
130
|
+
for _, task in result.items():
|
131
|
+
async_tasks.append(
|
132
|
+
DownloadGraphTask(
|
133
|
+
function_worker_input=FunctionWorkerInput(task=task),
|
134
|
+
downloader=self._downloader,
|
135
|
+
)
|
136
|
+
)
|
137
|
+
async_tasks.append(
|
138
|
+
asyncio.create_task(
|
139
|
+
self._task_store.get_runnable_tasks(),
|
140
|
+
name="get_runnable_tasks",
|
141
|
+
)
|
142
|
+
)
|
143
|
+
elif async_task.get_name() == "download_graph":
|
144
|
+
if async_task.exception():
|
145
|
+
logger.error(
|
146
|
+
"task_launcher_error, failed to download graph",
|
147
|
+
exc_info=async_task.exception(),
|
148
|
+
)
|
149
|
+
completed_task = CompletedTask(
|
150
|
+
task=async_task.function_worker_input.task,
|
151
|
+
task_outcome="failure",
|
152
|
+
)
|
153
|
+
self._task_store.complete(outcome=completed_task)
|
154
|
+
continue
|
155
|
+
async_task: DownloadGraphTask
|
156
|
+
function_worker_input: FunctionWorkerInput = (
|
157
|
+
async_task.function_worker_input
|
158
|
+
)
|
159
|
+
function_worker_input.graph = await async_task
|
160
|
+
async_tasks.append(
|
161
|
+
DownloadInputsTask(
|
162
|
+
function_worker_input=function_worker_input,
|
163
|
+
downloader=self._downloader,
|
164
|
+
)
|
165
|
+
)
|
166
|
+
elif async_task.get_name() == "download_inputs":
|
167
|
+
if async_task.exception():
|
168
|
+
logger.error(
|
169
|
+
"task_launcher_error, failed to download inputs",
|
170
|
+
exc_info=async_task.exception(),
|
171
|
+
)
|
172
|
+
completed_task = CompletedTask(
|
173
|
+
task=async_task.function_worker_input.task,
|
174
|
+
task_outcome="failure",
|
175
|
+
)
|
176
|
+
self._task_store.complete(outcome=completed_task)
|
177
|
+
continue
|
178
|
+
async_task: DownloadInputsTask
|
179
|
+
function_worker_input: FunctionWorkerInput = (
|
180
|
+
async_task.function_worker_input
|
181
|
+
)
|
182
|
+
function_worker_input.function_input = await async_task
|
183
|
+
async_tasks.append(
|
184
|
+
RunTask(
|
185
|
+
function_worker=self._function_worker,
|
186
|
+
function_worker_input=function_worker_input,
|
187
|
+
)
|
188
|
+
)
|
189
|
+
elif async_task.get_name() == "run_task":
|
190
|
+
if async_task.exception():
|
191
|
+
completed_task = CompletedTask(
|
192
|
+
task=async_task.function_worker_input.task,
|
193
|
+
task_outcome="failure",
|
194
|
+
stderr=str(async_task.exception()),
|
195
|
+
)
|
196
|
+
self._task_store.complete(outcome=completed_task)
|
197
|
+
continue
|
198
|
+
async_task: RunTask
|
199
|
+
try:
|
200
|
+
outputs: FunctionWorkerOutput = await async_task
|
201
|
+
if not outputs.success:
|
202
|
+
task_outcome = "failure"
|
203
|
+
else:
|
204
|
+
task_outcome = "success"
|
205
|
+
|
206
|
+
completed_task = CompletedTask(
|
207
|
+
task=async_task.function_worker_input.task,
|
208
|
+
task_outcome=task_outcome,
|
209
|
+
function_output=outputs.function_output,
|
210
|
+
router_output=outputs.router_output,
|
211
|
+
stdout=outputs.stdout,
|
212
|
+
stderr=outputs.stderr,
|
213
|
+
reducer=outputs.reducer,
|
214
|
+
)
|
215
|
+
self._task_store.complete(outcome=completed_task)
|
216
|
+
except Exception as e:
|
217
|
+
logger.error(
|
218
|
+
"failed to execute task",
|
219
|
+
task_id=async_task.function_worker_input.task.id,
|
220
|
+
exc_info=e,
|
221
|
+
)
|
222
|
+
completed_task = CompletedTask(
|
223
|
+
task=async_task.function_worker_input.task,
|
224
|
+
task_outcome="failure",
|
225
|
+
)
|
226
|
+
self._task_store.complete(outcome=completed_task)
|
227
|
+
continue
|
228
|
+
|
229
|
+
async def _main_loop(self):
|
230
|
+
"""Fetches incoming tasks from the server and starts their processing."""
|
231
|
+
self._should_run = True
|
232
|
+
while self._should_run:
|
233
|
+
try:
|
234
|
+
async for task in self._task_fetcher.run():
|
235
|
+
self._task_store.add_tasks([task])
|
236
|
+
except Exception as e:
|
237
|
+
logger.error("failed fetching tasks, retrying in 5 seconds", exc_info=e)
|
238
|
+
await asyncio.sleep(5)
|
239
|
+
continue
|
240
|
+
|
241
|
+
async def run(self):
|
242
|
+
import signal
|
243
|
+
|
244
|
+
asyncio.get_event_loop().add_signal_handler(
|
245
|
+
signal.SIGINT, self.shutdown, asyncio.get_event_loop()
|
246
|
+
)
|
247
|
+
asyncio.get_event_loop().add_signal_handler(
|
248
|
+
signal.SIGTERM, self.shutdown, asyncio.get_event_loop()
|
249
|
+
)
|
250
|
+
asyncio.create_task(self.task_launcher())
|
251
|
+
asyncio.create_task(self.task_completion_reporter())
|
252
|
+
await self._main_loop()
|
253
|
+
|
254
|
+
async def _shutdown(self, loop):
|
255
|
+
logger.info("shutting_down")
|
256
|
+
self._should_run = False
|
257
|
+
await self._function_worker.shutdown()
|
258
|
+
for task in asyncio.all_tasks(loop):
|
259
|
+
task.cancel()
|
260
|
+
|
261
|
+
def shutdown(self, loop):
|
262
|
+
loop.create_task(self._shutdown(loop))
|
@@ -1,8 +1,6 @@
|
|
1
1
|
from typing import Any, Dict, List, Optional
|
2
2
|
|
3
|
-
from pydantic import BaseModel
|
4
|
-
|
5
|
-
from indexify.functions_sdk.data_objects import IndexifyData
|
3
|
+
from pydantic import BaseModel
|
6
4
|
|
7
5
|
|
8
6
|
class Task(BaseModel):
|
@@ -21,7 +19,7 @@ class ExecutorMetadata(BaseModel):
|
|
21
19
|
executor_version: str
|
22
20
|
addr: str
|
23
21
|
image_name: str
|
24
|
-
|
22
|
+
image_hash: str
|
25
23
|
labels: Dict[str, Any]
|
26
24
|
|
27
25
|
|
@@ -29,10 +27,6 @@ class RouterOutput(BaseModel):
|
|
29
27
|
edges: List[str]
|
30
28
|
|
31
29
|
|
32
|
-
class FnOutput(BaseModel):
|
33
|
-
payload: Json
|
34
|
-
|
35
|
-
|
36
30
|
class TaskResult(BaseModel):
|
37
31
|
router_output: Optional[RouterOutput] = None
|
38
32
|
outcome: str
|