indexify 0.2.29__tar.gz → 0.2.31__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.29 → indexify-0.2.31}/PKG-INFO +1 -1
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/agent.py +16 -3
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/downloader.py +1 -1
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/task_reporter.py +49 -12
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/task_store.py +1 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/graph.py +6 -6
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/graph_definition.py +1 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/indexify_functions.py +13 -9
- {indexify-0.2.29 → indexify-0.2.31}/indexify/http_client.py +3 -1
- {indexify-0.2.29 → indexify-0.2.31}/indexify/remote_graph.py +7 -0
- {indexify-0.2.29 → indexify-0.2.31}/pyproject.toml +1 -5
- {indexify-0.2.29 → indexify-0.2.31}/LICENSE.txt +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/README.md +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/__init__.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/cli.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/common_util.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/data_loaders/__init__.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/data_loaders/local_directory_loader.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/data_loaders/url_loader.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/error.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/api_objects.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/executor_tasks.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/function_worker.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/image_dependency_installer.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/indexify_executor.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/executor/runtime_probes.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/data_objects.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/graph_validation.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/image.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/local_cache.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/object_serializer.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/functions_sdk/pipeline.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/remote_pipeline.py +0 -0
- {indexify-0.2.29 → indexify-0.2.31}/indexify/settings.py +0 -0
@@ -100,7 +100,9 @@ class ExtractorAgent:
|
|
100
100
|
self._server_addr = server_addr
|
101
101
|
self._base_url = f"{self._protocol}://{self._server_addr}"
|
102
102
|
self._code_path = code_path
|
103
|
-
self._downloader = Downloader(
|
103
|
+
self._downloader = Downloader(
|
104
|
+
code_path=code_path, base_url=self._base_url, config_path=self._config_path
|
105
|
+
)
|
104
106
|
self._max_queued_tasks = 10
|
105
107
|
self._task_reporter = TaskReporter(
|
106
108
|
base_url=self._base_url,
|
@@ -112,9 +114,15 @@ class ExtractorAgent:
|
|
112
114
|
console.print(Text("Starting task completion reporter", style="bold cyan"))
|
113
115
|
# We should copy only the keys and not the values
|
114
116
|
url = f"{self._protocol}://{self._server_addr}/write_content"
|
117
|
+
|
115
118
|
while True:
|
116
119
|
outcomes = await self._task_store.task_outcomes()
|
117
120
|
for task_outcome in outcomes:
|
121
|
+
retryStr = (
|
122
|
+
f"\nRetries: {task_outcome.reporting_retries}"
|
123
|
+
if task_outcome.reporting_retries > 0
|
124
|
+
else ""
|
125
|
+
)
|
118
126
|
outcome = task_outcome.task_outcome
|
119
127
|
style_outcome = (
|
120
128
|
f"[bold red] {outcome} [/]"
|
@@ -125,7 +133,9 @@ class ExtractorAgent:
|
|
125
133
|
Panel(
|
126
134
|
f"Reporting outcome of task: {task_outcome.task.id}, function: {task_outcome.task.compute_fn}\n"
|
127
135
|
f"Outcome: {style_outcome}\n"
|
128
|
-
f"Num Fn Outputs: {len(task_outcome.outputs or [])}
|
136
|
+
f"Num Fn Outputs: {len(task_outcome.outputs or [])}\n"
|
137
|
+
f"Router Output: {task_outcome.router_output}\n"
|
138
|
+
f"Retries: {task_outcome.reporting_retries}",
|
129
139
|
title="Task Completion",
|
130
140
|
border_style="info",
|
131
141
|
)
|
@@ -139,11 +149,14 @@ class ExtractorAgent:
|
|
139
149
|
console.print(
|
140
150
|
Panel(
|
141
151
|
f"Failed to report task {task_outcome.task.id}\n"
|
142
|
-
f"Exception: {e}\
|
152
|
+
f"Exception: {type(e).__name__}({e})\n"
|
153
|
+
f"Retries: {task_outcome.reporting_retries}\n"
|
154
|
+
"Retrying...",
|
143
155
|
title="Reporting Error",
|
144
156
|
border_style="error",
|
145
157
|
)
|
146
158
|
)
|
159
|
+
task_outcome.reporting_retries += 1
|
147
160
|
await asyncio.sleep(5)
|
148
161
|
continue
|
149
162
|
|
@@ -120,7 +120,7 @@ class Downloader:
|
|
120
120
|
deserialized_content = serializer.deserialize(response.content)
|
121
121
|
|
122
122
|
if reducer_url:
|
123
|
-
init_value =
|
123
|
+
init_value = self._client.get(reducer_url)
|
124
124
|
try:
|
125
125
|
init_value.raise_for_status()
|
126
126
|
except httpx.HTTPStatusError as e:
|
@@ -2,6 +2,8 @@ import io
|
|
2
2
|
from typing import Optional
|
3
3
|
|
4
4
|
import nanoid
|
5
|
+
from httpx import Timeout
|
6
|
+
from pydantic import BaseModel
|
5
7
|
from rich import print
|
6
8
|
|
7
9
|
from indexify.common_util import get_httpx_client
|
@@ -21,6 +23,15 @@ FORCE_MULTIPART = ForceMultipartDict()
|
|
21
23
|
UTF_8_CONTENT_TYPE = "application/octet-stream"
|
22
24
|
|
23
25
|
|
26
|
+
class ReportingData(BaseModel):
|
27
|
+
output_count: int = 0
|
28
|
+
output_total_bytes: int = 0
|
29
|
+
stdout_count: int = 0
|
30
|
+
stdout_total_bytes: int = 0
|
31
|
+
stderr_count: int = 0
|
32
|
+
stderr_total_bytes: int = 0
|
33
|
+
|
34
|
+
|
24
35
|
class TaskReporter:
|
25
36
|
def __init__(
|
26
37
|
self, base_url: str, executor_id: str, config_path: Optional[str] = None
|
@@ -30,11 +41,10 @@ class TaskReporter:
|
|
30
41
|
self._client = get_httpx_client(config_path)
|
31
42
|
|
32
43
|
def report_task_outcome(self, completed_task: CompletedTask):
|
44
|
+
|
45
|
+
report = ReportingData()
|
33
46
|
fn_outputs = []
|
34
47
|
for output in completed_task.outputs or []:
|
35
|
-
print(
|
36
|
-
f"[bold]task-reporter[/bold] uploading output of size: {len(output.payload)} bytes"
|
37
|
-
)
|
38
48
|
serializer = get_serializer(output.encoder)
|
39
49
|
serialized_output = serializer.serialize(output.payload)
|
40
50
|
fn_outputs.append(
|
@@ -43,11 +53,10 @@ class TaskReporter:
|
|
43
53
|
(nanoid.generate(), serialized_output, serializer.content_type),
|
44
54
|
)
|
45
55
|
)
|
56
|
+
report.output_count += 1
|
57
|
+
report.output_total_bytes += len(serialized_output)
|
46
58
|
|
47
59
|
if completed_task.stdout:
|
48
|
-
print(
|
49
|
-
f"[bold]task-reporter[/bold] uploading stdout of size: {len(completed_task.stdout)}"
|
50
|
-
)
|
51
60
|
fn_outputs.append(
|
52
61
|
(
|
53
62
|
"stdout",
|
@@ -58,11 +67,10 @@ class TaskReporter:
|
|
58
67
|
),
|
59
68
|
)
|
60
69
|
)
|
70
|
+
report.stdout_count += 1
|
71
|
+
report.stdout_total_bytes += len(completed_task.stdout)
|
61
72
|
|
62
73
|
if completed_task.stderr:
|
63
|
-
print(
|
64
|
-
f"[bold]task-reporter[/bold] uploading stderr of size: {len(completed_task.stderr)}"
|
65
|
-
)
|
66
74
|
fn_outputs.append(
|
67
75
|
(
|
68
76
|
"stderr",
|
@@ -73,6 +81,8 @@ class TaskReporter:
|
|
73
81
|
),
|
74
82
|
)
|
75
83
|
)
|
84
|
+
report.stderr_count += 1
|
85
|
+
report.stderr_total_bytes += len(completed_task.stderr)
|
76
86
|
|
77
87
|
router_output = (
|
78
88
|
ApiRouterOutput(edges=completed_task.router_output.edges)
|
@@ -93,7 +103,30 @@ class TaskReporter:
|
|
93
103
|
)
|
94
104
|
task_result_data = task_result.model_dump_json(exclude_none=True)
|
95
105
|
|
96
|
-
|
106
|
+
total_bytes = (
|
107
|
+
report.output_total_bytes
|
108
|
+
+ report.stdout_total_bytes
|
109
|
+
+ report.stderr_total_bytes
|
110
|
+
)
|
111
|
+
|
112
|
+
print(
|
113
|
+
f"[bold]task-reporter[/bold] reporting task outcome "
|
114
|
+
f"task_id={completed_task.task.id} retries={completed_task.reporting_retries} "
|
115
|
+
f"total_bytes={total_bytes} total_files={report.output_count + report.stdout_count + report.stderr_count} "
|
116
|
+
f"output_files={report.output_count} output_bytes={total_bytes} "
|
117
|
+
f"stdout_bytes={report.stdout_total_bytes} stderr_bytes={report.stderr_total_bytes} "
|
118
|
+
)
|
119
|
+
|
120
|
+
#
|
121
|
+
kwargs = {
|
122
|
+
"data": {"task_result": task_result_data},
|
123
|
+
# Use httpx default timeout of 5s for all timeout types.
|
124
|
+
# For read timeouts, use 5 minutes to allow for large file uploads.
|
125
|
+
"timeout": Timeout(
|
126
|
+
5.0,
|
127
|
+
read=5.0 * 60,
|
128
|
+
),
|
129
|
+
}
|
97
130
|
if fn_outputs and len(fn_outputs) > 0:
|
98
131
|
kwargs["files"] = fn_outputs
|
99
132
|
else:
|
@@ -104,11 +137,15 @@ class TaskReporter:
|
|
104
137
|
**kwargs,
|
105
138
|
)
|
106
139
|
except Exception as e:
|
107
|
-
print(
|
140
|
+
print(
|
141
|
+
f"[bold]task-reporter[/bold] failed to report task outcome retries={completed_task.reporting_retries} {type(e).__name__}({e})"
|
142
|
+
)
|
108
143
|
raise e
|
109
144
|
|
110
145
|
try:
|
111
146
|
response.raise_for_status()
|
112
147
|
except Exception as e:
|
113
|
-
print(
|
148
|
+
print(
|
149
|
+
f"[bold]task-reporter[/bold] failed to report task outcome retries={completed_task.reporting_retries} {response.text}"
|
150
|
+
)
|
114
151
|
raise e
|
@@ -101,9 +101,9 @@ class Graph:
|
|
101
101
|
return self
|
102
102
|
|
103
103
|
if issubclass(indexify_fn, IndexifyFunction) and indexify_fn.accumulate:
|
104
|
-
self.accumulator_zero_values[
|
105
|
-
indexify_fn.
|
106
|
-
|
104
|
+
self.accumulator_zero_values[indexify_fn.name] = (
|
105
|
+
indexify_fn.accumulate().model_dump()
|
106
|
+
)
|
107
107
|
|
108
108
|
self.nodes[indexify_fn.name] = indexify_fn
|
109
109
|
return self
|
@@ -244,9 +244,9 @@ class Graph:
|
|
244
244
|
queue = deque([(self._start_node, initial_input)])
|
245
245
|
while queue:
|
246
246
|
node_name, input = queue.popleft()
|
247
|
-
function_outputs: Union[
|
248
|
-
|
249
|
-
|
247
|
+
function_outputs: Union[FunctionCallResult, RouterCallResult] = (
|
248
|
+
self._invoke_fn(node_name, input)
|
249
|
+
)
|
250
250
|
self._log_local_exec_tracebacks(function_outputs)
|
251
251
|
if isinstance(function_outputs, RouterCallResult):
|
252
252
|
for edge in function_outputs.edges:
|
@@ -46,6 +46,7 @@ class ComputeGraphMetadata(BaseModel):
|
|
46
46
|
edges: Dict[str, List[str]]
|
47
47
|
accumulator_zero_values: Dict[str, bytes] = {}
|
48
48
|
runtime_information: RuntimeInformation
|
49
|
+
replaying: bool = False
|
49
50
|
|
50
51
|
def get_input_payload_serializer(self):
|
51
52
|
return get_serializer(self.start_node.compute_fn.encoder)
|
@@ -137,9 +137,11 @@ def indexify_router(
|
|
137
137
|
|
138
138
|
attrs = {
|
139
139
|
"name": name if name else fn.__name__,
|
140
|
-
"description":
|
141
|
-
|
142
|
-
|
140
|
+
"description": (
|
141
|
+
description
|
142
|
+
if description
|
143
|
+
else (fn.__doc__ or "").strip().replace("\n", "")
|
144
|
+
),
|
143
145
|
"image": image,
|
144
146
|
"placement_constraints": placement_constraints,
|
145
147
|
"encoder": encoder,
|
@@ -174,9 +176,11 @@ def indexify_function(
|
|
174
176
|
|
175
177
|
attrs = {
|
176
178
|
"name": name if name else fn.__name__,
|
177
|
-
"description":
|
178
|
-
|
179
|
-
|
179
|
+
"description": (
|
180
|
+
description
|
181
|
+
if description
|
182
|
+
else (fn.__doc__ or "").strip().replace("\n", "")
|
183
|
+
),
|
180
184
|
"image": image,
|
181
185
|
"placement_constraints": placement_constraints,
|
182
186
|
"accumulate": accumulate,
|
@@ -205,9 +209,9 @@ class IndexifyFunctionWrapper:
|
|
205
209
|
indexify_function: Union[IndexifyFunction, IndexifyRouter],
|
206
210
|
context: GraphInvocationContext,
|
207
211
|
):
|
208
|
-
self.indexify_function: Union[
|
209
|
-
|
210
|
-
|
212
|
+
self.indexify_function: Union[IndexifyFunction, IndexifyRouter] = (
|
213
|
+
indexify_function()
|
214
|
+
)
|
211
215
|
self.indexify_function._ctx = context
|
212
216
|
|
213
217
|
def get_output_model(self) -> Any:
|
@@ -135,7 +135,9 @@ class IndexifyClient:
|
|
135
135
|
|
136
136
|
def _add_api_key(self, kwargs):
|
137
137
|
if self._api_key:
|
138
|
-
|
138
|
+
if "headers" not in kwargs:
|
139
|
+
kwargs["headers"] = {}
|
140
|
+
kwargs["headers"]["Authorization"] = f"Bearer {self._api_key}"
|
139
141
|
|
140
142
|
def _get(self, endpoint: str, **kwargs) -> httpx.Response:
|
141
143
|
self._add_api_key(kwargs)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import Any, List, Optional
|
2
2
|
|
3
3
|
from indexify.functions_sdk.graph import ComputeGraphMetadata, Graph
|
4
|
+
from indexify.functions_sdk.graph_definition import ComputeGraphMetadata
|
4
5
|
|
5
6
|
from .http_client import IndexifyClient
|
6
7
|
from .settings import DEFAULT_SERVICE_URL
|
@@ -55,6 +56,12 @@ class RemoteGraph:
|
|
55
56
|
**kwargs
|
56
57
|
)
|
57
58
|
|
59
|
+
def metadata(self) -> ComputeGraphMetadata:
|
60
|
+
"""
|
61
|
+
Get the metadata of the graph.
|
62
|
+
"""
|
63
|
+
return self._client.graph(self._name)
|
64
|
+
|
58
65
|
def replay_invocations(self):
|
59
66
|
"""
|
60
67
|
Replay all the graph previous runs/invocations on the latest version of the graph.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "indexify"
|
3
|
-
version = "0.2.
|
3
|
+
version = "0.2.31"
|
4
4
|
description = "Python Client for Indexify"
|
5
5
|
authors = ["Tensorlake Inc. <support@tensorlake.ai>"]
|
6
6
|
license = "Apache 2.0"
|
@@ -27,10 +27,6 @@ jsonpickle = "^4.0.0"
|
|
27
27
|
[tool.poetry.dev-dependencies]
|
28
28
|
black = "^22.3.0"
|
29
29
|
pylint = "^2.4.0"
|
30
|
-
pytest = "^7.1.1"
|
31
|
-
pytest-cov = "^4.0.0"
|
32
|
-
pytest-runner = "^6.0.0"
|
33
|
-
pytest-watch = "^4.2.0"
|
34
30
|
parameterized = "^0.9.0"
|
35
31
|
|
36
32
|
[build-system]
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|