indexify 0.2.20__py3-none-any.whl → 0.2.22__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 +6 -2
- indexify/error.py +5 -0
- indexify/executor/agent.py +92 -8
- indexify/executor/image_dependency_installer.py +52 -0
- indexify/executor/task_reporter.py +1 -1
- indexify/http_client.py +5 -1
- {indexify-0.2.20.dist-info → indexify-0.2.22.dist-info}/METADATA +1 -1
- {indexify-0.2.20.dist-info → indexify-0.2.22.dist-info}/RECORD +11 -10
- {indexify-0.2.20.dist-info → indexify-0.2.22.dist-info}/LICENSE.txt +0 -0
- {indexify-0.2.20.dist-info → indexify-0.2.22.dist-info}/WHEEL +0 -0
- {indexify-0.2.20.dist-info → indexify-0.2.22.dist-info}/entry_points.txt +0 -0
indexify/cli.py
CHANGED
@@ -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:
|
indexify/error.py
CHANGED
indexify/executor/agent.py
CHANGED
@@ -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=
|
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(
|
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(
|
indexify/http_client.py
CHANGED
@@ -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,17 +1,18 @@
|
|
1
1
|
indexify/__init__.py,sha256=xYjdqZQ9CEtV-s4PJhj_6Vx_dfg_qknWHkLqOSVom8g,623
|
2
|
-
indexify/cli.py,sha256=
|
2
|
+
indexify/cli.py,sha256=sR-9wc3J_46TsOV424VD6TwKZkEg9MHiflvAJB1VyeY,8420
|
3
3
|
indexify/data_loaders/__init__.py,sha256=Y5NEuseTcYAICRiweYw5wBQ2m2YplbsY21I7df-rdi4,1339
|
4
4
|
indexify/data_loaders/local_directory_loader.py,sha256=fCrgj5drnW71ZUdDDvcB1-VJjIs1w6Q8sEW0HSGSAiA,1247
|
5
5
|
indexify/data_loaders/url_loader.py,sha256=32SERljcq1Xsi4RdLz2dgyk2TER5pQPTtXl3gUzwHbY,1533
|
6
|
-
indexify/error.py,sha256=
|
7
|
-
indexify/executor/agent.py,sha256=
|
6
|
+
indexify/error.py,sha256=mBEz_fj2sdtTQol-VfwvD8Syu7x9uvYSy2nne4Gr2GI,237
|
7
|
+
indexify/executor/agent.py,sha256=nSJ9vIWqY74pL5cEy7Rvb_xDatqtgEmxT4sJ65z3Rb0,17984
|
8
8
|
indexify/executor/api_objects.py,sha256=QMlHbcL-ZOAMVD71XOJqD_ZIy-NjrUf1DAvi0hFYVg0,836
|
9
9
|
indexify/executor/downloader.py,sha256=3mEDdluTzspsLGAZtFHZOVuyKOzT3CSema2kIK6Z1yU,4005
|
10
10
|
indexify/executor/executor_tasks.py,sha256=gAZ2pvza1YwGlaR1o_tJW4SXtdCgK7sLJgp4W7rOjR0,1834
|
11
11
|
indexify/executor/function_worker.py,sha256=pCGn13rg4dEykzmwYNyCTkewrpyQXQR1cH6n2Hx5Lfc,5813
|
12
|
+
indexify/executor/image_dependency_installer.py,sha256=ditkQQ8rMrtQgZZ7Nw5wDn4I0d-qk8Q2irL-cU-4gOY,1449
|
12
13
|
indexify/executor/indexify_executor.py,sha256=2Ut_VX-Su_lm4b4aEROyRJ3gXx-uFHA-V7EN0sWiARE,771
|
13
14
|
indexify/executor/runtime_probes.py,sha256=JY0FoxtlQ9sgsE8gBKWM5h3R1TWkYENhNF0HR2KkV4Q,1704
|
14
|
-
indexify/executor/task_reporter.py,sha256=
|
15
|
+
indexify/executor/task_reporter.py,sha256=0ujOJrN7aW9PiVH0gr1rGqadfZE9yfV318Ll3eahBng,3721
|
15
16
|
indexify/executor/task_store.py,sha256=q8s2gImsFffWeXQR0mk1Xlo1Aj_2GfclNPjQ2EA_YBo,3984
|
16
17
|
indexify/functions_sdk/data_objects.py,sha256=CQZMzYiV7l6dyzFkYquWQHqdte6JnC7XA3i2ZyvPvgQ,844
|
17
18
|
indexify/functions_sdk/graph.py,sha256=L8jtuSGaUk05JgT_cQX-n_1H9t84lcPa1v4XXlpuJa0,11143
|
@@ -22,12 +23,12 @@ indexify/functions_sdk/indexify_functions.py,sha256=KLeRQY5IcrVp5T3K4yQ8Uu_CLL-C
|
|
22
23
|
indexify/functions_sdk/local_cache.py,sha256=cNWF67zbhbTJe3g86hyLBy3Rqzs6dNvp2SjLazGZWvw,1348
|
23
24
|
indexify/functions_sdk/object_serializer.py,sha256=Zz4GobW3ZamBBtFDF76QxU3TP6oJNdWnhsfKd0OUFoc,1660
|
24
25
|
indexify/functions_sdk/pipeline.py,sha256=7hDatRK-SCHYvttf2Vj5YFyiJEVU0OOXEZBOIQenSmk,847
|
25
|
-
indexify/http_client.py,sha256=
|
26
|
+
indexify/http_client.py,sha256=pFJ4i40_GgzGJP9ih_iCHNUJQlSsWUiCLBMT3aIegOg,13887
|
26
27
|
indexify/remote_graph.py,sha256=NOotayhw_x4-mAw1c3ooNe7gFE_q3QJ9qaO0t0Sb8sE,4397
|
27
28
|
indexify/remote_pipeline.py,sha256=FW7IAv3r24OOpiqlprw3kuFrpdkqi6Ic4_tT26FThjA,761
|
28
29
|
indexify/settings.py,sha256=LSaWZ0ADIVmUv6o6dHWRC3-Ry5uLbCw2sBSg1e_U7UM,99
|
29
|
-
indexify-0.2.
|
30
|
-
indexify-0.2.
|
31
|
-
indexify-0.2.
|
32
|
-
indexify-0.2.
|
33
|
-
indexify-0.2.
|
30
|
+
indexify-0.2.22.dist-info/LICENSE.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
31
|
+
indexify-0.2.22.dist-info/METADATA,sha256=PTlq-SzEMf1hcd09SwyB7aBF4BvsQgVPyPK8PQoGrCU,6199
|
32
|
+
indexify-0.2.22.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
33
|
+
indexify-0.2.22.dist-info/entry_points.txt,sha256=Pih7WV-XMpAzI5dEvROcpLr-ybVhd9Y-AtuzBKUdcDs,49
|
34
|
+
indexify-0.2.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|