indexify 0.2.20__tar.gz → 0.2.22__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 (34) hide show
  1. {indexify-0.2.20 → indexify-0.2.22}/PKG-INFO +1 -1
  2. {indexify-0.2.20 → indexify-0.2.22}/indexify/cli.py +6 -2
  3. indexify-0.2.22/indexify/error.py +8 -0
  4. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/agent.py +92 -8
  5. indexify-0.2.22/indexify/executor/image_dependency_installer.py +52 -0
  6. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/task_reporter.py +1 -1
  7. {indexify-0.2.20 → indexify-0.2.22}/indexify/http_client.py +5 -1
  8. {indexify-0.2.20 → indexify-0.2.22}/pyproject.toml +1 -1
  9. indexify-0.2.20/indexify/error.py +0 -3
  10. {indexify-0.2.20 → indexify-0.2.22}/LICENSE.txt +0 -0
  11. {indexify-0.2.20 → indexify-0.2.22}/README.md +0 -0
  12. {indexify-0.2.20 → indexify-0.2.22}/indexify/__init__.py +0 -0
  13. {indexify-0.2.20 → indexify-0.2.22}/indexify/data_loaders/__init__.py +0 -0
  14. {indexify-0.2.20 → indexify-0.2.22}/indexify/data_loaders/local_directory_loader.py +0 -0
  15. {indexify-0.2.20 → indexify-0.2.22}/indexify/data_loaders/url_loader.py +0 -0
  16. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/api_objects.py +0 -0
  17. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/downloader.py +0 -0
  18. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/executor_tasks.py +0 -0
  19. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/function_worker.py +0 -0
  20. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/indexify_executor.py +0 -0
  21. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/runtime_probes.py +0 -0
  22. {indexify-0.2.20 → indexify-0.2.22}/indexify/executor/task_store.py +0 -0
  23. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/data_objects.py +0 -0
  24. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/graph.py +0 -0
  25. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/graph_definition.py +0 -0
  26. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/graph_validation.py +0 -0
  27. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/image.py +0 -0
  28. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/indexify_functions.py +0 -0
  29. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/local_cache.py +0 -0
  30. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/object_serializer.py +0 -0
  31. {indexify-0.2.20 → indexify-0.2.22}/indexify/functions_sdk/pipeline.py +0 -0
  32. {indexify-0.2.20 → indexify-0.2.22}/indexify/remote_graph.py +0 -0
  33. {indexify-0.2.20 → indexify-0.2.22}/indexify/remote_pipeline.py +0 -0
  34. {indexify-0.2.20 → indexify-0.2.22}/indexify/settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: indexify
3
- Version: 0.2.20
3
+ Version: 0.2.22
4
4
  Summary: Python Client for Indexify
5
5
  Home-page: https://github.com/tensorlakeai/indexify
6
6
  License: Apache 2.0
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import io
3
2
  import os
4
3
  import shutil
5
4
  import signal
@@ -168,6 +167,9 @@ def executor(
168
167
  executor_cache: Optional[str] = typer.Option(
169
168
  "~/.indexify/executor_cache", help="Path to the executor cache directory"
170
169
  ),
170
+ name_alias: Optional[str] = typer.Option(
171
+ None, help="Name alias for the executor if it's spun up with the base image"
172
+ ),
171
173
  ):
172
174
  id = nanoid.generate()
173
175
  console.print(
@@ -176,7 +178,8 @@ def executor(
176
178
  f"Config path: {config_path}\n"
177
179
  f"Server address: {server_addr}\n"
178
180
  f"Executor ID: {id}\n"
179
- f"Executor cache: {executor_cache}",
181
+ f"Executor cache: {executor_cache}\n"
182
+ f"Name Alias: {name_alias}",
180
183
  title="Agent Configuration",
181
184
  border_style="info",
182
185
  )
@@ -197,6 +200,7 @@ def executor(
197
200
  server_addr=server_addr,
198
201
  config_path=config_path,
199
202
  code_path=executor_cache,
203
+ name_alias=name_alias,
200
204
  )
201
205
 
202
206
  try:
@@ -0,0 +1,8 @@
1
+ class ApiException(Exception):
2
+ def __init__(self, message: str) -> None:
3
+ super().__init__(message)
4
+
5
+
6
+ class GraphStillProcessing(Exception):
7
+ def __init__(self) -> None:
8
+ super().__init__("graph is still processing")
@@ -3,9 +3,12 @@ import json
3
3
  import ssl
4
4
  from concurrent.futures.process import BrokenProcessPool
5
5
  from importlib.metadata import version
6
+ import traceback
6
7
  from typing import Dict, List, Optional
7
8
 
8
9
  import httpx
10
+ from indexify.functions_sdk.graph_definition import ComputeGraphMetadata
11
+ from indexify.http_client import IndexifyClient
9
12
  import yaml
10
13
  from httpx_sse import aconnect_sse
11
14
  from pydantic import BaseModel
@@ -19,6 +22,7 @@ from indexify.functions_sdk.data_objects import (
19
22
  IndexifyData,
20
23
  RouterOutput,
21
24
  )
25
+ from . import image_dependency_installer
22
26
 
23
27
  from .api_objects import ExecutorMetadata, Task
24
28
  from .downloader import DownloadedInputs, Downloader
@@ -27,6 +31,7 @@ from .function_worker import FunctionWorker
27
31
  from .runtime_probes import ProbeInfo, RuntimeProbes
28
32
  from .task_reporter import TaskReporter
29
33
  from .task_store import CompletedTask, TaskStore
34
+ from ..functions_sdk.image import ImageInformation
30
35
 
31
36
  custom_theme = Theme(
32
37
  {
@@ -58,7 +63,24 @@ class ExtractorAgent:
58
63
  function_worker: FunctionWorker,
59
64
  server_addr: str = "localhost:8900",
60
65
  config_path: Optional[str] = None,
66
+ name_alias: Optional[str] = None,
61
67
  ):
68
+ self.name_alias = name_alias
69
+
70
+ self._probe = RuntimeProbes()
71
+
72
+ runtime_probe: ProbeInfo = self._probe.probe()
73
+ self._require_image_bootstrap = (
74
+ True
75
+ if (runtime_probe.is_default_executor and self.name_alias is not None)
76
+ else False
77
+ )
78
+ self._executor_bootstrap_failed = False
79
+
80
+ console.print(
81
+ f"Require Bootstrap? {self._require_image_bootstrap}", style="cyan bold"
82
+ )
83
+
62
84
  self.num_workers = num_workers
63
85
  self._use_tls = False
64
86
  if config_path:
@@ -99,7 +121,6 @@ class ExtractorAgent:
99
121
  self._task_reporter = TaskReporter(
100
122
  base_url=self._base_url, executor_id=self._executor_id
101
123
  )
102
- self._probe = RuntimeProbes()
103
124
 
104
125
  async def task_completion_reporter(self):
105
126
  console.print(Text("Starting task completion reporter", style="bold cyan"))
@@ -145,15 +166,55 @@ class ExtractorAgent:
145
166
  async def task_launcher(self):
146
167
  async_tasks: List[asyncio.Task] = []
147
168
  fn_queue: List[FunctionInput] = []
169
+
148
170
  async_tasks.append(
149
171
  asyncio.create_task(
150
172
  self._task_store.get_runnable_tasks(), name="get_runnable_tasks"
151
173
  )
152
174
  )
175
+
153
176
  while True:
154
177
  fn: FunctionInput
155
178
  for fn in fn_queue:
156
179
  task: Task = self._task_store.get_task(fn.task_id)
180
+
181
+ if self._executor_bootstrap_failed:
182
+ completed_task = CompletedTask(
183
+ task=task,
184
+ outputs=[],
185
+ task_outcome="failure",
186
+ )
187
+ self._task_store.complete(outcome=completed_task)
188
+
189
+ continue
190
+
191
+ # Bootstrap this executor. Fail the task if we can't.
192
+ if self._require_image_bootstrap:
193
+ try:
194
+ image_info = await _get_image_info_for_compute_graph(
195
+ task, self._protocol, self._server_addr
196
+ )
197
+ image_dependency_installer.executor_image_builder(
198
+ image_info, self.name_alias
199
+ )
200
+ self._require_image_bootstrap = False
201
+ except Exception as e:
202
+ console.print(
203
+ Text("Failed to bootstrap the executor ", style="red bold")
204
+ + Text(f"Exception: {traceback.format_exc()}", style="red")
205
+ )
206
+
207
+ self._executor_bootstrap_failed = True
208
+
209
+ completed_task = CompletedTask(
210
+ task=task,
211
+ outputs=[],
212
+ task_outcome="failure",
213
+ )
214
+ self._task_store.complete(outcome=completed_task)
215
+
216
+ continue
217
+
157
218
  async_tasks.append(
158
219
  ExtractTask(
159
220
  function_worker=self._function_worker,
@@ -309,18 +370,19 @@ class ExtractorAgent:
309
370
 
310
371
  runtime_probe: ProbeInfo = self._probe.probe()
311
372
 
312
- # Inspect the image
313
- if runtime_probe.is_default_executor:
314
- # install dependencies
315
- # rewrite the image name
316
- pass
317
-
318
373
  executor_version = version("indexify")
374
+
375
+ image_name = (
376
+ self.name_alias
377
+ if self.name_alias is not None
378
+ else runtime_probe.image_name
379
+ )
380
+
319
381
  data = ExecutorMetadata(
320
382
  id=self._executor_id,
321
383
  executor_version=executor_version,
322
384
  addr="",
323
- image_name=runtime_probe.image_name,
385
+ image_name=image_name,
324
386
  labels=runtime_probe.labels,
325
387
  ).model_dump()
326
388
 
@@ -372,3 +434,25 @@ class ExtractorAgent:
372
434
  def shutdown(self, loop):
373
435
  self._function_worker.shutdown()
374
436
  loop.create_task(self._shutdown(loop))
437
+
438
+
439
+ async def _get_image_info_for_compute_graph(
440
+ task: Task, protocol, server_addr
441
+ ) -> ImageInformation:
442
+ namespace = task.namespace
443
+ graph_name: str = task.compute_graph
444
+ compute_fn_name: str = task.compute_fn
445
+
446
+ http_client = IndexifyClient(
447
+ service_url=f"{protocol}://{server_addr}", namespace=namespace
448
+ )
449
+ compute_graph: ComputeGraphMetadata = http_client.graph(graph_name)
450
+
451
+ console.print(
452
+ Text(
453
+ f"Compute_fn name {compute_fn_name}, ComputeGraph {compute_graph} \n",
454
+ style="red yellow",
455
+ )
456
+ )
457
+
458
+ return compute_graph.nodes[compute_fn_name].compute_fn.image_information
@@ -0,0 +1,52 @@
1
+ import os
2
+ import subprocess
3
+
4
+ from rich.console import Console
5
+ from rich.text import Text
6
+ from rich.theme import Theme
7
+
8
+ from indexify.functions_sdk.image import ImageInformation
9
+
10
+
11
+ custom_theme = Theme(
12
+ {
13
+ "info": "cyan",
14
+ "warning": "yellow",
15
+ "error": "red",
16
+ "success": "green",
17
+ }
18
+ )
19
+
20
+ console = Console(theme=custom_theme)
21
+
22
+
23
+ def _record_image_name(name: str):
24
+ dir_path = os.path.expanduser("~/.indexify/")
25
+ file_path = os.path.expanduser("~/.indexify/image_name")
26
+ os.makedirs(dir_path, exist_ok=True)
27
+
28
+ with open(file_path, "w") as file:
29
+ file.write(name)
30
+
31
+
32
+ def _install_dependencies(run_str: str):
33
+ # Throw error to the caller if these subprocesses fail.
34
+ proc = subprocess.run(run_str.split())
35
+ if proc.returncode != 0:
36
+ raise Exception(f"Unable to install dep `{run_str}`")
37
+
38
+
39
+ def executor_image_builder(image_info: ImageInformation, name_alias: str):
40
+ console.print(Text("Attempting Executor Bootstrap.", style="red bold"))
41
+
42
+ run_strs = image_info.run_strs
43
+ console.print(Text("Attempting to install dependencies.", style="red bold"))
44
+
45
+ for run_str in run_strs:
46
+ console.print(Text(f"Attempting {run_str}", style="red bold"))
47
+ _install_dependencies(run_str)
48
+
49
+ console.print(Text("Install dependencies done.", style="red bold"))
50
+
51
+ console.print(Text(f"Recording image name {name_alias}", style="red bold"))
52
+ _record_image_name(name_alias)
@@ -30,7 +30,7 @@ class TaskReporter:
30
30
  fn_outputs = []
31
31
  for output in completed_task.outputs or []:
32
32
  print(
33
- f"[bold]task-reporter[/bold] uploading output of size: {len(output_bytes)} bytes"
33
+ f"[bold]task-reporter[/bold] uploading output of size: {len(output.payload)} bytes"
34
34
  )
35
35
  output_bytes = MsgPackSerializer.serialize(output)
36
36
  fn_outputs.append(
@@ -10,7 +10,7 @@ from httpx_sse import connect_sse
10
10
  from pydantic import BaseModel, Json
11
11
  from rich import print
12
12
 
13
- from indexify.error import ApiException
13
+ from indexify.error import ApiException, GraphStillProcessing
14
14
  from indexify.functions_sdk.data_objects import IndexifyData
15
15
  from indexify.functions_sdk.graph import ComputeGraphMetadata, Graph
16
16
  from indexify.functions_sdk.indexify_functions import IndexifyFunction
@@ -36,7 +36,9 @@ class GraphOutputMetadata(BaseModel):
36
36
 
37
37
 
38
38
  class GraphOutputs(BaseModel):
39
+ status: str
39
40
  outputs: List[GraphOutputMetadata]
41
+ cursor: Optional[str] = None
40
42
 
41
43
 
42
44
  class IndexifyClient:
@@ -329,6 +331,8 @@ class IndexifyClient:
329
331
  )
330
332
  response.raise_for_status()
331
333
  graph_outputs = GraphOutputs(**response.json())
334
+ if graph_outputs.status == "pending":
335
+ raise GraphStillProcessing()
332
336
  outputs = []
333
337
  for output in graph_outputs.outputs:
334
338
  if output.compute_fn == fn_name:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "indexify"
3
- version = "0.2.20"
3
+ version = "0.2.22"
4
4
  description = "Python Client for Indexify"
5
5
  authors = ["Tensorlake Inc. <support@tensorlake.ai>"]
6
6
  license = "Apache 2.0"
@@ -1,3 +0,0 @@
1
- class ApiException(Exception):
2
- def __init__(self, message: str) -> None:
3
- super().__init__(message)
File without changes
File without changes