indexify 0.2.22__tar.gz → 0.2.23__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.22 → indexify-0.2.23}/PKG-INFO +1 -1
- {indexify-0.2.22 → indexify-0.2.23}/indexify/__init__.py +4 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/cli.py +6 -3
- {indexify-0.2.22 → indexify-0.2.23}/indexify/error.py +1 -1
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/agent.py +26 -8
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/api_objects.py +1 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/executor_tasks.py +1 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/function_worker.py +42 -5
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/image_dependency_installer.py +18 -6
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/runtime_probes.py +11 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/task_reporter.py +1 -1
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/graph.py +13 -5
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/indexify_functions.py +45 -7
- {indexify-0.2.22 → indexify-0.2.23}/indexify/http_client.py +20 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/remote_graph.py +7 -2
- {indexify-0.2.22 → indexify-0.2.23}/pyproject.toml +1 -1
- {indexify-0.2.22 → indexify-0.2.23}/LICENSE.txt +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/README.md +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/data_loaders/__init__.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/data_loaders/local_directory_loader.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/data_loaders/url_loader.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/downloader.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/indexify_executor.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/executor/task_store.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/data_objects.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/graph_definition.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/graph_validation.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/image.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/local_cache.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/object_serializer.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/functions_sdk/pipeline.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/remote_pipeline.py +0 -0
- {indexify-0.2.22 → indexify-0.2.23}/indexify/settings.py +0 -0
@@ -2,6 +2,8 @@ from . import data_loaders
|
|
2
2
|
from .functions_sdk.graph import Graph
|
3
3
|
from .functions_sdk.image import Image
|
4
4
|
from .functions_sdk.indexify_functions import (
|
5
|
+
IndexifyFunction,
|
6
|
+
get_ctx,
|
5
7
|
indexify_function,
|
6
8
|
indexify_router,
|
7
9
|
)
|
@@ -19,6 +21,8 @@ __all__ = [
|
|
19
21
|
"RemotePipeline",
|
20
22
|
"Image",
|
21
23
|
"indexify_function",
|
24
|
+
"get_ctx",
|
25
|
+
"IndexifyFunction",
|
22
26
|
"indexify_router",
|
23
27
|
"DEFAULT_SERVICE_URL",
|
24
28
|
"IndexifyClient",
|
@@ -170,6 +170,9 @@ def executor(
|
|
170
170
|
name_alias: Optional[str] = typer.Option(
|
171
171
|
None, help="Name alias for the executor if it's spun up with the base image"
|
172
172
|
),
|
173
|
+
image_version: Optional[int] = typer.Option(
|
174
|
+
"1", help="Requested Image Version for this executor"
|
175
|
+
),
|
173
176
|
):
|
174
177
|
id = nanoid.generate()
|
175
178
|
console.print(
|
@@ -179,13 +182,13 @@ def executor(
|
|
179
182
|
f"Server address: {server_addr}\n"
|
180
183
|
f"Executor ID: {id}\n"
|
181
184
|
f"Executor cache: {executor_cache}\n"
|
182
|
-
f"Name Alias: {name_alias}"
|
185
|
+
f"Name Alias: {name_alias}"
|
186
|
+
f"Image Version: {image_version}\n",
|
183
187
|
title="Agent Configuration",
|
184
188
|
border_style="info",
|
185
189
|
)
|
186
190
|
)
|
187
191
|
|
188
|
-
function_worker = FunctionWorker(workers=workers)
|
189
192
|
from pathlib import Path
|
190
193
|
|
191
194
|
executor_cache = Path(executor_cache).expanduser().absolute()
|
@@ -196,11 +199,11 @@ def executor(
|
|
196
199
|
agent = ExtractorAgent(
|
197
200
|
id,
|
198
201
|
num_workers=workers,
|
199
|
-
function_worker=function_worker,
|
200
202
|
server_addr=server_addr,
|
201
203
|
config_path=config_path,
|
202
204
|
code_path=executor_cache,
|
203
205
|
name_alias=name_alias,
|
206
|
+
image_version=image_version,
|
204
207
|
)
|
205
208
|
|
206
209
|
try:
|
@@ -1,14 +1,12 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
3
|
import ssl
|
4
|
+
import traceback
|
4
5
|
from concurrent.futures.process import BrokenProcessPool
|
5
6
|
from importlib.metadata import version
|
6
|
-
import traceback
|
7
7
|
from typing import Dict, List, Optional
|
8
8
|
|
9
9
|
import httpx
|
10
|
-
from indexify.functions_sdk.graph_definition import ComputeGraphMetadata
|
11
|
-
from indexify.http_client import IndexifyClient
|
12
10
|
import yaml
|
13
11
|
from httpx_sse import aconnect_sse
|
14
12
|
from pydantic import BaseModel
|
@@ -22,8 +20,11 @@ from indexify.functions_sdk.data_objects import (
|
|
22
20
|
IndexifyData,
|
23
21
|
RouterOutput,
|
24
22
|
)
|
25
|
-
from . import
|
23
|
+
from indexify.functions_sdk.graph_definition import ComputeGraphMetadata
|
24
|
+
from indexify.http_client import IndexifyClient
|
26
25
|
|
26
|
+
from ..functions_sdk.image import ImageInformation
|
27
|
+
from . import image_dependency_installer
|
27
28
|
from .api_objects import ExecutorMetadata, Task
|
28
29
|
from .downloader import DownloadedInputs, Downloader
|
29
30
|
from .executor_tasks import DownloadGraphTask, DownloadInputTask, ExtractTask
|
@@ -31,7 +32,6 @@ from .function_worker import FunctionWorker
|
|
31
32
|
from .runtime_probes import ProbeInfo, RuntimeProbes
|
32
33
|
from .task_reporter import TaskReporter
|
33
34
|
from .task_store import CompletedTask, TaskStore
|
34
|
-
from ..functions_sdk.image import ImageInformation
|
35
35
|
|
36
36
|
custom_theme = Theme(
|
37
37
|
{
|
@@ -60,12 +60,13 @@ class ExtractorAgent:
|
|
60
60
|
executor_id: str,
|
61
61
|
num_workers,
|
62
62
|
code_path: str,
|
63
|
-
function_worker: FunctionWorker,
|
64
63
|
server_addr: str = "localhost:8900",
|
65
64
|
config_path: Optional[str] = None,
|
66
65
|
name_alias: Optional[str] = None,
|
66
|
+
image_version: Optional[int] = None,
|
67
67
|
):
|
68
68
|
self.name_alias = name_alias
|
69
|
+
self.image_version = image_version
|
69
70
|
|
70
71
|
self._probe = RuntimeProbes()
|
71
72
|
|
@@ -111,7 +112,12 @@ class ExtractorAgent:
|
|
111
112
|
|
112
113
|
self._task_store: TaskStore = TaskStore()
|
113
114
|
self._executor_id = executor_id
|
114
|
-
self._function_worker =
|
115
|
+
self._function_worker = FunctionWorker(
|
116
|
+
workers=num_workers,
|
117
|
+
indexify_client=IndexifyClient(
|
118
|
+
service_url=f"{self._protocol}://{server_addr}"
|
119
|
+
),
|
120
|
+
)
|
115
121
|
self._has_registered = False
|
116
122
|
self._server_addr = server_addr
|
117
123
|
self._base_url = f"{self._protocol}://{self._server_addr}"
|
@@ -195,7 +201,7 @@ class ExtractorAgent:
|
|
195
201
|
task, self._protocol, self._server_addr
|
196
202
|
)
|
197
203
|
image_dependency_installer.executor_image_builder(
|
198
|
-
image_info, self.name_alias
|
204
|
+
image_info, self.name_alias, self.image_version
|
199
205
|
)
|
200
206
|
self._require_image_bootstrap = False
|
201
207
|
except Exception as e:
|
@@ -378,11 +384,18 @@ class ExtractorAgent:
|
|
378
384
|
else runtime_probe.image_name
|
379
385
|
)
|
380
386
|
|
387
|
+
image_version: int = (
|
388
|
+
self.image_version
|
389
|
+
if self.image_version is not None
|
390
|
+
else runtime_probe.image_version
|
391
|
+
)
|
392
|
+
|
381
393
|
data = ExecutorMetadata(
|
382
394
|
id=self._executor_id,
|
383
395
|
executor_version=executor_version,
|
384
396
|
addr="",
|
385
397
|
image_name=image_name,
|
398
|
+
image_version=image_version,
|
386
399
|
labels=runtime_probe.labels,
|
387
400
|
).model_dump()
|
388
401
|
|
@@ -406,6 +419,11 @@ class ExtractorAgent:
|
|
406
419
|
json=data,
|
407
420
|
headers={"Content-Type": "application/json"},
|
408
421
|
) as event_source:
|
422
|
+
if not event_source.response.is_success:
|
423
|
+
resp = await event_source.response.aread().decode('utf-8')
|
424
|
+
console.print(f"failed to register: {str(resp)}")
|
425
|
+
await asyncio.sleep(5)
|
426
|
+
continue
|
409
427
|
console.print(
|
410
428
|
Text("executor registered successfully", style="bold green")
|
411
429
|
)
|
@@ -6,6 +6,7 @@ import cloudpickle
|
|
6
6
|
from pydantic import BaseModel
|
7
7
|
from rich import print
|
8
8
|
|
9
|
+
from indexify import IndexifyClient
|
9
10
|
from indexify.functions_sdk.data_objects import (
|
10
11
|
FunctionWorkerOutput,
|
11
12
|
IndexifyData,
|
@@ -13,6 +14,7 @@ from indexify.functions_sdk.data_objects import (
|
|
13
14
|
)
|
14
15
|
from indexify.functions_sdk.indexify_functions import (
|
15
16
|
FunctionCallResult,
|
17
|
+
GraphInvocationContext,
|
16
18
|
IndexifyFunctionWrapper,
|
17
19
|
RouterCallResult,
|
18
20
|
)
|
@@ -44,7 +46,13 @@ class FunctionOutput(BaseModel):
|
|
44
46
|
|
45
47
|
|
46
48
|
def _load_function(
|
47
|
-
namespace: str,
|
49
|
+
namespace: str,
|
50
|
+
graph_name: str,
|
51
|
+
fn_name: str,
|
52
|
+
code_path: str,
|
53
|
+
version: int,
|
54
|
+
invocation_id: str,
|
55
|
+
indexify_client: IndexifyClient,
|
48
56
|
):
|
49
57
|
"""Load an extractor to the memory: extractor_wrapper_map."""
|
50
58
|
global function_wrapper_map
|
@@ -54,18 +62,28 @@ def _load_function(
|
|
54
62
|
with open(code_path, "rb") as f:
|
55
63
|
code = f.read()
|
56
64
|
pickled_functions = cloudpickle.loads(code)
|
65
|
+
context = GraphInvocationContext(
|
66
|
+
invocation_id=invocation_id,
|
67
|
+
graph_name=graph_name,
|
68
|
+
graph_version=str(version),
|
69
|
+
indexify_client=indexify_client,
|
70
|
+
)
|
57
71
|
function_wrapper = IndexifyFunctionWrapper(
|
58
|
-
cloudpickle.loads(pickled_functions[fn_name])
|
72
|
+
cloudpickle.loads(pickled_functions[fn_name]),
|
73
|
+
context,
|
59
74
|
)
|
60
75
|
function_wrapper_map[key] = function_wrapper
|
61
76
|
|
62
77
|
|
63
78
|
class FunctionWorker:
|
64
|
-
def __init__(
|
79
|
+
def __init__(
|
80
|
+
self, workers: int = 1, indexify_client: IndexifyClient = None
|
81
|
+
) -> None:
|
65
82
|
self._executor: concurrent.futures.ProcessPoolExecutor = (
|
66
83
|
concurrent.futures.ProcessPoolExecutor(max_workers=workers)
|
67
84
|
)
|
68
85
|
self._workers = workers
|
86
|
+
self._indexify_client = indexify_client
|
69
87
|
|
70
88
|
async def async_submit(
|
71
89
|
self,
|
@@ -76,10 +94,19 @@ class FunctionWorker:
|
|
76
94
|
code_path: str,
|
77
95
|
version: int,
|
78
96
|
init_value: Optional[IndexifyData] = None,
|
97
|
+
invocation_id: Optional[str] = None,
|
79
98
|
) -> FunctionWorkerOutput:
|
80
99
|
try:
|
81
100
|
result = _run_function(
|
82
|
-
namespace,
|
101
|
+
namespace,
|
102
|
+
graph_name,
|
103
|
+
fn_name,
|
104
|
+
input,
|
105
|
+
code_path,
|
106
|
+
version,
|
107
|
+
init_value,
|
108
|
+
invocation_id,
|
109
|
+
self._indexify_client,
|
83
110
|
)
|
84
111
|
# TODO - bring back running in a separate process
|
85
112
|
except Exception as e:
|
@@ -113,6 +140,8 @@ def _run_function(
|
|
113
140
|
code_path: str,
|
114
141
|
version: int,
|
115
142
|
init_value: Optional[IndexifyData] = None,
|
143
|
+
invocation_id: Optional[str] = None,
|
144
|
+
indexify_client: Optional[IndexifyClient] = None,
|
116
145
|
) -> FunctionOutput:
|
117
146
|
import io
|
118
147
|
from contextlib import redirect_stderr, redirect_stdout
|
@@ -131,7 +160,15 @@ def _run_function(
|
|
131
160
|
try:
|
132
161
|
key = f"{namespace}/{graph_name}/{version}/{fn_name}"
|
133
162
|
if key not in function_wrapper_map:
|
134
|
-
_load_function(
|
163
|
+
_load_function(
|
164
|
+
namespace,
|
165
|
+
graph_name,
|
166
|
+
fn_name,
|
167
|
+
code_path,
|
168
|
+
version,
|
169
|
+
invocation_id,
|
170
|
+
indexify_client,
|
171
|
+
)
|
135
172
|
|
136
173
|
fn = function_wrapper_map[key]
|
137
174
|
if (
|
@@ -7,7 +7,6 @@ from rich.theme import Theme
|
|
7
7
|
|
8
8
|
from indexify.functions_sdk.image import ImageInformation
|
9
9
|
|
10
|
-
|
11
10
|
custom_theme = Theme(
|
12
11
|
{
|
13
12
|
"info": "cyan",
|
@@ -20,14 +19,19 @@ custom_theme = Theme(
|
|
20
19
|
console = Console(theme=custom_theme)
|
21
20
|
|
22
21
|
|
23
|
-
def _record_image_name(name: str):
|
22
|
+
def _record_image_name(name: str, version: int):
|
24
23
|
dir_path = os.path.expanduser("~/.indexify/")
|
24
|
+
|
25
25
|
file_path = os.path.expanduser("~/.indexify/image_name")
|
26
26
|
os.makedirs(dir_path, exist_ok=True)
|
27
|
-
|
28
27
|
with open(file_path, "w") as file:
|
29
28
|
file.write(name)
|
30
29
|
|
30
|
+
file_path = os.path.expanduser("~/.indexify/image_version")
|
31
|
+
os.makedirs(dir_path, exist_ok=True)
|
32
|
+
with open(file_path, "w") as file:
|
33
|
+
file.write(str(version))
|
34
|
+
|
31
35
|
|
32
36
|
def _install_dependencies(run_str: str):
|
33
37
|
# Throw error to the caller if these subprocesses fail.
|
@@ -36,7 +40,9 @@ def _install_dependencies(run_str: str):
|
|
36
40
|
raise Exception(f"Unable to install dep `{run_str}`")
|
37
41
|
|
38
42
|
|
39
|
-
def executor_image_builder(
|
43
|
+
def executor_image_builder(
|
44
|
+
image_info: ImageInformation, name_alias: str, image_version: int
|
45
|
+
):
|
40
46
|
console.print(Text("Attempting Executor Bootstrap.", style="red bold"))
|
41
47
|
|
42
48
|
run_strs = image_info.run_strs
|
@@ -48,5 +54,11 @@ def executor_image_builder(image_info: ImageInformation, name_alias: str):
|
|
48
54
|
|
49
55
|
console.print(Text("Install dependencies done.", style="red bold"))
|
50
56
|
|
51
|
-
console.print(
|
52
|
-
|
57
|
+
console.print(
|
58
|
+
Text(
|
59
|
+
f"Recording image name {name_alias} and version {image_version}",
|
60
|
+
style="red bold",
|
61
|
+
)
|
62
|
+
)
|
63
|
+
|
64
|
+
_record_image_name(name_alias, image_version)
|
@@ -6,10 +6,12 @@ from typing import Any, Dict, Tuple
|
|
6
6
|
from pydantic import BaseModel
|
7
7
|
|
8
8
|
DEFAULT_EXECUTOR = "tensorlake/indexify-executor-default"
|
9
|
+
DEFAULT_VERSION = 1
|
9
10
|
|
10
11
|
|
11
12
|
class ProbeInfo(BaseModel):
|
12
13
|
image_name: str
|
14
|
+
image_version: int
|
13
15
|
python_major_version: int
|
14
16
|
labels: Dict[str, Any] = {}
|
15
17
|
is_default_executor: bool
|
@@ -18,6 +20,7 @@ class ProbeInfo(BaseModel):
|
|
18
20
|
class RuntimeProbes:
|
19
21
|
def __init__(self) -> None:
|
20
22
|
self._image_name = self._read_image_name()
|
23
|
+
self._image_version = self._read_image_version()
|
21
24
|
self._os_name = platform.system()
|
22
25
|
self._architecture = platform.machine()
|
23
26
|
(
|
@@ -32,6 +35,13 @@ class RuntimeProbes:
|
|
32
35
|
return file.read().strip()
|
33
36
|
return DEFAULT_EXECUTOR
|
34
37
|
|
38
|
+
def _read_image_version(self) -> int:
|
39
|
+
file_path = os.path.expanduser("~/.indexify/image_version")
|
40
|
+
if os.path.exists(file_path):
|
41
|
+
with open(file_path, "r") as file:
|
42
|
+
return int(file.read().strip())
|
43
|
+
return DEFAULT_VERSION
|
44
|
+
|
35
45
|
def _get_python_version(self) -> Tuple[int, int]:
|
36
46
|
version_info = sys.version_info
|
37
47
|
return version_info.major, version_info.minor
|
@@ -50,6 +60,7 @@ class RuntimeProbes:
|
|
50
60
|
|
51
61
|
return ProbeInfo(
|
52
62
|
image_name=self._image_name,
|
63
|
+
image_version=self._image_version,
|
53
64
|
python_major_version=self._python_version_major,
|
54
65
|
labels=labels,
|
55
66
|
is_default_executor=self._is_default_executor(),
|
@@ -32,7 +32,7 @@ class TaskReporter:
|
|
32
32
|
print(
|
33
33
|
f"[bold]task-reporter[/bold] uploading output of size: {len(output.payload)} bytes"
|
34
34
|
)
|
35
|
-
output_bytes = MsgPackSerializer.serialize(output)
|
35
|
+
output_bytes = MsgPackSerializer.serialize(output)
|
36
36
|
fn_outputs.append(
|
37
37
|
("node_outputs", (nanoid.generate(), io.BytesIO(output_bytes)))
|
38
38
|
)
|
@@ -33,6 +33,7 @@ from .indexify_functions import (
|
|
33
33
|
IndexifyFunction,
|
34
34
|
IndexifyFunctionWrapper,
|
35
35
|
IndexifyRouter,
|
36
|
+
GraphInvocationContext,
|
36
37
|
)
|
37
38
|
from .local_cache import CacheAwareFunctionWrapper
|
38
39
|
from .object_serializer import get_serializer
|
@@ -96,9 +97,9 @@ class Graph:
|
|
96
97
|
return self
|
97
98
|
|
98
99
|
if issubclass(indexify_fn, IndexifyFunction) and indexify_fn.accumulate:
|
99
|
-
self.accumulator_zero_values[
|
100
|
-
indexify_fn.
|
101
|
-
)
|
100
|
+
self.accumulator_zero_values[
|
101
|
+
indexify_fn.name
|
102
|
+
] = indexify_fn.accumulate().model_dump()
|
102
103
|
|
103
104
|
self.nodes[indexify_fn.name] = indexify_fn
|
104
105
|
return self
|
@@ -214,7 +215,13 @@ class Graph:
|
|
214
215
|
}
|
215
216
|
self._results[input.id] = outputs
|
216
217
|
enable_cache = kwargs.get("enable_cache", True)
|
217
|
-
|
218
|
+
ctx = GraphInvocationContext(
|
219
|
+
invocation_id=input.id,
|
220
|
+
graph_name=self.name,
|
221
|
+
graph_version="1",
|
222
|
+
indexify_client=None,
|
223
|
+
)
|
224
|
+
self._run(input, outputs, enable_cache, ctx)
|
218
225
|
return input.id
|
219
226
|
|
220
227
|
def _run(
|
@@ -222,6 +229,7 @@ class Graph:
|
|
222
229
|
initial_input: IndexifyData,
|
223
230
|
outputs: Dict[str, List[bytes]],
|
224
231
|
enable_cache: bool,
|
232
|
+
ctx: GraphInvocationContext,
|
225
233
|
):
|
226
234
|
accumulator_values = self._accumulator_values[initial_input.id]
|
227
235
|
queue = deque([(self._start_node, initial_input)])
|
@@ -229,7 +237,7 @@ class Graph:
|
|
229
237
|
node_name, input = queue.popleft()
|
230
238
|
node = self.nodes[node_name]
|
231
239
|
function_outputs: FunctionCallResult = IndexifyFunctionWrapper(
|
232
|
-
node
|
240
|
+
node, context=ctx
|
233
241
|
).invoke_fn_ser(node_name, input, accumulator_values.get(node_name, None))
|
234
242
|
if function_outputs.traceback_msg is not None:
|
235
243
|
print(function_outputs.traceback_msg)
|
@@ -2,7 +2,6 @@ import inspect
|
|
2
2
|
import re
|
3
3
|
import sys
|
4
4
|
import traceback
|
5
|
-
from abc import ABC, abstractmethod
|
6
5
|
from functools import update_wrapper
|
7
6
|
from typing import (
|
8
7
|
Any,
|
@@ -18,14 +17,37 @@ from typing import (
|
|
18
17
|
)
|
19
18
|
|
20
19
|
import msgpack
|
21
|
-
from pydantic import BaseModel
|
20
|
+
from pydantic import BaseModel, Field, PrivateAttr, model_validator
|
22
21
|
from typing_extensions import get_type_hints
|
23
22
|
|
24
|
-
from .data_objects import IndexifyData
|
23
|
+
from .data_objects import IndexifyData
|
25
24
|
from .image import DEFAULT_IMAGE_3_10, Image
|
26
25
|
from .object_serializer import CloudPickleSerializer, get_serializer
|
27
26
|
|
28
27
|
|
28
|
+
class GraphInvocationContext(BaseModel):
|
29
|
+
invocation_id: str
|
30
|
+
graph_name: str
|
31
|
+
graph_version: str
|
32
|
+
indexify_client: Optional[Any] = Field(default=None) # avoids circular import
|
33
|
+
_local_state: Dict[str, Any] = PrivateAttr(default_factory=dict)
|
34
|
+
|
35
|
+
def set_state_key(self, key: str, value: Any) -> None:
|
36
|
+
if self.indexify_client is None:
|
37
|
+
self._local_state[key] = value
|
38
|
+
return
|
39
|
+
self.indexify_client.set_state_key(
|
40
|
+
self.graph_name, self.invocation_id, key, value
|
41
|
+
)
|
42
|
+
|
43
|
+
def get_state_key(self, key: str) -> Any:
|
44
|
+
if self.indexify_client is None:
|
45
|
+
return self._local_state.get(key)
|
46
|
+
return self.indexify_client.get_state_key(
|
47
|
+
self.graph_name, self.invocation_id, key
|
48
|
+
)
|
49
|
+
|
50
|
+
|
29
51
|
def format_filtered_traceback(exc_info=None):
|
30
52
|
"""
|
31
53
|
Format a traceback excluding indexify_functions.py lines.
|
@@ -205,10 +227,15 @@ class RouterCallResult(BaseModel):
|
|
205
227
|
|
206
228
|
|
207
229
|
class IndexifyFunctionWrapper:
|
208
|
-
def __init__(
|
209
|
-
self
|
210
|
-
|
211
|
-
|
230
|
+
def __init__(
|
231
|
+
self,
|
232
|
+
indexify_function: Union[IndexifyFunction, IndexifyRouter],
|
233
|
+
context: GraphInvocationContext,
|
234
|
+
):
|
235
|
+
self.indexify_function: Union[
|
236
|
+
IndexifyFunction, IndexifyRouter
|
237
|
+
] = indexify_function()
|
238
|
+
self.indexify_function._ctx = context
|
212
239
|
|
213
240
|
def get_output_model(self) -> Any:
|
214
241
|
if not isinstance(self.indexify_function, IndexifyFunction):
|
@@ -322,3 +349,14 @@ class IndexifyFunctionWrapper:
|
|
322
349
|
payload = list(payload.values())[0]
|
323
350
|
return arg_type.model_validate(payload)
|
324
351
|
return payload
|
352
|
+
|
353
|
+
|
354
|
+
def get_ctx() -> GraphInvocationContext:
|
355
|
+
frame = inspect.currentframe()
|
356
|
+
caller_frame = frame.f_back.f_back
|
357
|
+
function_instance = caller_frame.f_locals["self"]
|
358
|
+
del frame
|
359
|
+
del caller_frame
|
360
|
+
if isinstance(function_instance, IndexifyFunctionWrapper):
|
361
|
+
return function_instance.indexify_function._ctx
|
362
|
+
return function_instance._ctx
|
@@ -198,6 +198,23 @@ class IndexifyClient:
|
|
198
198
|
namespaces.append(item["name"])
|
199
199
|
return namespaces
|
200
200
|
|
201
|
+
def set_state_key(
|
202
|
+
self, compute_graph: str, invocation_id: str, key: str, value: Json
|
203
|
+
) -> None:
|
204
|
+
response = self._post(
|
205
|
+
f"internal/namespaces/{self.namespace}/compute_graphs/{compute_graph}/invocations/{invocation_id}/ctx",
|
206
|
+
json={"key": key, "value": value},
|
207
|
+
)
|
208
|
+
response.raise_for_status()
|
209
|
+
|
210
|
+
def get_state_key(self, compute_graph: str, invocation_id: str, key: str) -> Json:
|
211
|
+
response = self._get(
|
212
|
+
f"internal/namespaces/{self.namespace}/compute_graphs/{compute_graph}/invocations/{invocation_id}/ctx",
|
213
|
+
json={"key": key},
|
214
|
+
)
|
215
|
+
response.raise_for_status()
|
216
|
+
return response.json().get("value")
|
217
|
+
|
201
218
|
@classmethod
|
202
219
|
def new_namespace(
|
203
220
|
cls, namespace: str, server_addr: Optional[str] = "http://localhost:8900"
|
@@ -251,6 +268,9 @@ class IndexifyClient:
|
|
251
268
|
data=ser_input,
|
252
269
|
params=params,
|
253
270
|
) as event_source:
|
271
|
+
if not event_source.response.is_success:
|
272
|
+
resp = event_source.response.read().decode("utf-8")
|
273
|
+
raise Exception(f"failed to invoke graph: {resp}")
|
254
274
|
for sse in event_source.iter_sse():
|
255
275
|
obj = json.loads(sse.data)
|
256
276
|
for k, v in obj.items():
|
@@ -84,7 +84,12 @@ class RemoteGraph:
|
|
84
84
|
return cls(name=g.name, server_url=server_url, client=client)
|
85
85
|
|
86
86
|
@classmethod
|
87
|
-
def by_name(
|
87
|
+
def by_name(
|
88
|
+
cls,
|
89
|
+
name: str,
|
90
|
+
server_url: Optional[str] = DEFAULT_SERVICE_URL,
|
91
|
+
client: Optional[IndexifyClient] = None,
|
92
|
+
):
|
88
93
|
"""
|
89
94
|
Create a handle to call a RemoteGraph by name.
|
90
95
|
|
@@ -104,7 +109,7 @@ class RemoteGraph:
|
|
104
109
|
) -> List[Any]:
|
105
110
|
"""
|
106
111
|
Returns the extracted objects by a graph for an ingested object.
|
107
|
-
|
112
|
+
|
108
113
|
- If the extractor name is provided, only the objects extracted by that extractor are returned.
|
109
114
|
- If the extractor name is not provided, all the extracted objects are returned for the input object.
|
110
115
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|