indexify 0.2.40__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.
Files changed (58) hide show
  1. {indexify-0.2.40 → indexify-0.2.41}/PKG-INFO +3 -1
  2. {indexify-0.2.40 → indexify-0.2.41}/indexify/cli.py +92 -52
  3. indexify-0.2.41/indexify/executor/agent.py +262 -0
  4. {indexify-0.2.40 → indexify-0.2.41}/indexify/executor/api_objects.py +2 -8
  5. indexify-0.2.41/indexify/executor/downloader.py +165 -0
  6. indexify-0.2.41/indexify/executor/executor_tasks.py +58 -0
  7. indexify-0.2.41/indexify/executor/function_executor/function_executor.py +32 -0
  8. indexify-0.2.41/indexify/executor/function_executor/function_executor_factory.py +26 -0
  9. indexify-0.2.41/indexify/executor/function_executor/function_executor_map.py +91 -0
  10. indexify-0.2.41/indexify/executor/function_executor/process_function_executor.py +64 -0
  11. indexify-0.2.41/indexify/executor/function_executor/process_function_executor_factory.py +102 -0
  12. indexify-0.2.41/indexify/executor/function_worker.py +255 -0
  13. {indexify-0.2.40 → indexify-0.2.41}/indexify/executor/runtime_probes.py +9 -8
  14. indexify-0.2.41/indexify/executor/task_fetcher.py +80 -0
  15. {indexify-0.2.40 → indexify-0.2.41}/indexify/executor/task_reporter.py +18 -25
  16. {indexify-0.2.40 → indexify-0.2.41}/indexify/executor/task_store.py +35 -16
  17. indexify-0.2.41/indexify/function_executor/function_executor_service.py +86 -0
  18. indexify-0.2.41/indexify/function_executor/handlers/run_function/function_inputs_loader.py +54 -0
  19. indexify-0.2.41/indexify/function_executor/handlers/run_function/handler.py +149 -0
  20. indexify-0.2.41/indexify/function_executor/handlers/run_function/request_validator.py +24 -0
  21. indexify-0.2.41/indexify/function_executor/handlers/run_function/response_helper.py +98 -0
  22. indexify-0.2.41/indexify/function_executor/initialize_request_validator.py +22 -0
  23. indexify-0.2.41/indexify/function_executor/proto/configuration.py +13 -0
  24. indexify-0.2.41/indexify/function_executor/proto/function_executor.proto +70 -0
  25. indexify-0.2.41/indexify/function_executor/proto/function_executor_pb2.py +53 -0
  26. indexify-0.2.41/indexify/function_executor/proto/function_executor_pb2.pyi +125 -0
  27. indexify-0.2.41/indexify/function_executor/proto/function_executor_pb2_grpc.py +163 -0
  28. indexify-0.2.41/indexify/function_executor/proto/message_validator.py +38 -0
  29. indexify-0.2.41/indexify/function_executor/server.py +31 -0
  30. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/data_objects.py +0 -9
  31. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/graph.py +10 -11
  32. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/graph_definition.py +2 -2
  33. indexify-0.2.41/indexify/functions_sdk/image.py +77 -0
  34. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/indexify_functions.py +5 -5
  35. {indexify-0.2.40 → indexify-0.2.41}/indexify/http_client.py +15 -23
  36. indexify-0.2.41/indexify/logging.py +32 -0
  37. {indexify-0.2.40 → indexify-0.2.41}/pyproject.toml +3 -1
  38. indexify-0.2.40/indexify/executor/agent.py +0 -350
  39. indexify-0.2.40/indexify/executor/downloader.py +0 -126
  40. indexify-0.2.40/indexify/executor/executor_tasks.py +0 -73
  41. indexify-0.2.40/indexify/executor/function_worker.py +0 -212
  42. indexify-0.2.40/indexify/executor/indexify_executor.py +0 -32
  43. indexify-0.2.40/indexify/functions_sdk/image.py +0 -72
  44. {indexify-0.2.40 → indexify-0.2.41}/LICENSE.txt +0 -0
  45. {indexify-0.2.40 → indexify-0.2.41}/README.md +0 -0
  46. {indexify-0.2.40 → indexify-0.2.41}/indexify/__init__.py +0 -0
  47. {indexify-0.2.40 → indexify-0.2.41}/indexify/common_util.py +0 -0
  48. {indexify-0.2.40 → indexify-0.2.41}/indexify/data_loaders/__init__.py +0 -0
  49. {indexify-0.2.40 → indexify-0.2.41}/indexify/data_loaders/local_directory_loader.py +0 -0
  50. {indexify-0.2.40 → indexify-0.2.41}/indexify/data_loaders/url_loader.py +0 -0
  51. {indexify-0.2.40 → indexify-0.2.41}/indexify/error.py +0 -0
  52. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/graph_validation.py +0 -0
  53. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/local_cache.py +0 -0
  54. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/object_serializer.py +0 -0
  55. {indexify-0.2.40 → indexify-0.2.41}/indexify/functions_sdk/pipeline.py +0 -0
  56. {indexify-0.2.40 → indexify-0.2.41}/indexify/remote_graph.py +0 -0
  57. {indexify-0.2.40 → indexify-0.2.41}/indexify/remote_pipeline.py +0 -0
  58. {indexify-0.2.40 → 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.40
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.executor.function_worker import FunctionWorker
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
- DEFAULT_IMAGE_3_10,
24
- DEFAULT_IMAGE_3_11,
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
- _build_image(image=DEFAULT_IMAGE_3_10)
154
- _build_image(image=DEFAULT_IMAGE_3_11)
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
- workers: Annotated[
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="Name alias for the executor if it's spun up with the base image"
194
+ None, help="Image name override for the executor"
178
195
  ),
179
- image_version: Optional[int] = typer.Option(
180
- "1", help="Requested Image Version for this executor"
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
- processors = [
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
- logging.info(
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
- image_version=image_version,
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
- image_version=image_version,
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
- logging.info("graceful shutdown")
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
- docker_file = f"""
250
- FROM {image._base_image}
251
-
252
- RUN mkdir -p ~/.indexify
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 touch ~/.indexify/image_name
300
+ docker_contents.extend(["RUN " + i for i in image._run_strs])
255
301
 
256
- RUN echo {image._image_name} > ~/.indexify/image_name
257
-
258
- WORKDIR /app
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
- run_strs = ["RUN " + i for i in image._run_strs]
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, Json
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
- image_version: int
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