labtasker-client 2.2.0__tar.gz → 2.4.0__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.
- labtasker_client-2.4.0/PKG-INFO +17 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/pyproject.toml +9 -6
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/__init__.py +3 -1
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/cli.py +29 -4
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/client.py +18 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/config.py +6 -1
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/execution.py +40 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/journal.py +3 -1
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/models.py +38 -4
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/worker.py +45 -0
- labtasker_client-2.2.0/PKG-INFO +0 -14
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/.gitignore +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/LICENSE +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/__main__.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/api.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/binding.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/command_template.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/command_worker.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/errors.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/local.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/observations.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/paths.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/py.typed +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/tee.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/types.py +0 -0
- {labtasker_client-2.2.0 → labtasker_client-2.4.0}/src/labtasker/validation.py +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: labtasker-client
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: A small task queue for parallel model inference and evaluation
|
|
5
|
+
Project-URL: Homepage, https://github.com/luocfprime/labtasker
|
|
6
|
+
Project-URL: Repository, https://github.com/luocfprime/labtasker.git
|
|
7
|
+
Author-email: lcf <luocfprime@gmail.com>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: httpx[socks]<1,>=0.25.2
|
|
12
|
+
Requires-Dist: packaging>=21.3
|
|
13
|
+
Requires-Dist: pydantic<3,>=2.12; python_version >= '3.14'
|
|
14
|
+
Requires-Dist: pydantic<3,>=2.5.3; python_version < '3.13'
|
|
15
|
+
Requires-Dist: pydantic<3,>=2.9; python_version >= '3.13' and python_version < '3.14'
|
|
16
|
+
Requires-Dist: tomli>=2.0.1; python_version < '3.11'
|
|
17
|
+
Requires-Dist: typer<1,>=0.19
|
|
@@ -4,17 +4,20 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "labtasker-client"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.4.0"
|
|
8
8
|
description = "A small task queue for parallel model inference and evaluation"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
12
12
|
authors = [{ name = "lcf", email = "luocfprime@gmail.com" }]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"httpx[socks]>=0.
|
|
15
|
-
"packaging>=
|
|
16
|
-
"pydantic>=2.
|
|
17
|
-
"
|
|
14
|
+
"httpx[socks]>=0.25.2,<1",
|
|
15
|
+
"packaging>=21.3",
|
|
16
|
+
"pydantic>=2.5.3,<3; python_version < '3.13'",
|
|
17
|
+
"pydantic>=2.9,<3; python_version >= '3.13' and python_version < '3.14'",
|
|
18
|
+
"pydantic>=2.12,<3; python_version >= '3.14'",
|
|
19
|
+
"tomli>=2.0.1; python_version < '3.11'",
|
|
20
|
+
"typer>=0.19,<1",
|
|
18
21
|
]
|
|
19
22
|
|
|
20
23
|
[project.urls]
|
|
@@ -30,6 +30,7 @@ from labtasker.errors import (
|
|
|
30
30
|
from labtasker.execution import (
|
|
31
31
|
cancellation_requested,
|
|
32
32
|
finish,
|
|
33
|
+
report_progress,
|
|
33
34
|
set_force_stop_timeout,
|
|
34
35
|
task_info,
|
|
35
36
|
)
|
|
@@ -48,7 +49,7 @@ from labtasker.models import (
|
|
|
48
49
|
from labtasker.types import JSONValue, TaskOrderField, TaskStatus, TaskUpdate
|
|
49
50
|
from labtasker.worker import loop
|
|
50
51
|
|
|
51
|
-
__version__ = "2.
|
|
52
|
+
__version__ = "2.4.0"
|
|
52
53
|
|
|
53
54
|
__all__ = [
|
|
54
55
|
"APIError",
|
|
@@ -87,6 +88,7 @@ __all__ = [
|
|
|
87
88
|
"list_tasks",
|
|
88
89
|
"list_workers",
|
|
89
90
|
"loop",
|
|
91
|
+
"report_progress",
|
|
90
92
|
"requeue_task",
|
|
91
93
|
"set_force_stop_timeout",
|
|
92
94
|
"submit_task",
|
|
@@ -7,7 +7,6 @@ from typing import Annotated, Any, TypeVar, cast
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
from pydantic import BaseModel
|
|
10
|
-
from typer._click.core import Context as ClickContext
|
|
11
10
|
from typer.core import TyperCommand
|
|
12
11
|
|
|
13
12
|
from labtasker import __version__
|
|
@@ -16,6 +15,7 @@ from labtasker.command_template import TemplateSyntaxError
|
|
|
16
15
|
from labtasker.command_worker import run_command_worker
|
|
17
16
|
from labtasker.config import resolve_config
|
|
18
17
|
from labtasker.errors import LabtaskerError
|
|
18
|
+
from labtasker.execution import report_progress as report_current_progress
|
|
19
19
|
from labtasker.types import TaskOrderField, TaskStatus, TaskUpdate
|
|
20
20
|
from labtasker.validation import RequestValidationError, validate_grouping, validate_json_object
|
|
21
21
|
|
|
@@ -82,10 +82,12 @@ def main(
|
|
|
82
82
|
class _SeparatedCommand(TyperCommand):
|
|
83
83
|
"""Require the explicit boundary between Worker options and child argv."""
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
# Typer 0.26 vendored Click, so its internal Context type differs from the
|
|
86
|
+
# public typer.Context used by earlier supported releases.
|
|
87
|
+
def collect_usage_pieces(self, ctx: Any) -> list[str]:
|
|
86
88
|
return [*super().collect_usage_pieces(ctx), "--", "COMMAND", "[ARG...]"]
|
|
87
89
|
|
|
88
|
-
def parse_args(self, ctx:
|
|
90
|
+
def parse_args(self, ctx: Any, args: list[str]) -> list[str]:
|
|
89
91
|
ctx.meta["labtasker_command_separator"] = "--" in args
|
|
90
92
|
return super().parse_args(ctx, args)
|
|
91
93
|
|
|
@@ -168,11 +170,34 @@ def worker_loop(
|
|
|
168
170
|
raise typer.Exit(1) from error
|
|
169
171
|
|
|
170
172
|
|
|
173
|
+
@app.command("progress")
|
|
174
|
+
def progress_report(
|
|
175
|
+
data: Annotated[
|
|
176
|
+
str,
|
|
177
|
+
typer.Option(help="Latest progress as one strict JSON object."),
|
|
178
|
+
],
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Replace the current Task run's progress snapshot.
|
|
181
|
+
|
|
182
|
+
This command is available inside a command launched by ``labtasker loop``.
|
|
183
|
+
It prints whether the best-effort report was accepted; transport failures
|
|
184
|
+
and confirmed revocation return ``reported: false`` without failing the
|
|
185
|
+
command workload.
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
progress = _json_object(data, option="--data")
|
|
189
|
+
reported = _invoke(lambda: report_current_progress(progress))
|
|
190
|
+
except RuntimeError as error:
|
|
191
|
+
typer.echo(str(error), err=True)
|
|
192
|
+
raise typer.Exit(1) from error
|
|
193
|
+
_write_json({"reported": reported})
|
|
194
|
+
|
|
195
|
+
|
|
171
196
|
@task_app.command("submit")
|
|
172
197
|
def task_submit(
|
|
173
198
|
args: Annotated[
|
|
174
199
|
str,
|
|
175
|
-
typer.Option(help="Task arguments as one strict JSON object."),
|
|
200
|
+
typer.Option(metavar="<str>", help="Task arguments as one strict JSON object."),
|
|
176
201
|
] = "{}",
|
|
177
202
|
name: Annotated[
|
|
178
203
|
str | None,
|
|
@@ -589,6 +589,24 @@ class Client:
|
|
|
589
589
|
parser=lambda response: _parse_none(response, {204}),
|
|
590
590
|
)
|
|
591
591
|
|
|
592
|
+
def _report_progress(
|
|
593
|
+
self,
|
|
594
|
+
*,
|
|
595
|
+
task_id: str,
|
|
596
|
+
run_id: str,
|
|
597
|
+
progress: dict[str, JSONValue],
|
|
598
|
+
queue: str | None = None,
|
|
599
|
+
) -> None:
|
|
600
|
+
normalized = validate_json_object(progress, field="progress")
|
|
601
|
+
self._run_action(
|
|
602
|
+
"progress",
|
|
603
|
+
task_id=task_id,
|
|
604
|
+
run_id=run_id,
|
|
605
|
+
queue=queue,
|
|
606
|
+
body={"progress": normalized},
|
|
607
|
+
parser=lambda response: _parse_none(response, {204}),
|
|
608
|
+
)
|
|
609
|
+
|
|
592
610
|
def _fail(
|
|
593
611
|
self,
|
|
594
612
|
*,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
-
import
|
|
4
|
+
import sys
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Literal, TypedDict
|
|
@@ -9,6 +9,11 @@ from urllib.parse import urlsplit, urlunsplit
|
|
|
9
9
|
|
|
10
10
|
import httpx
|
|
11
11
|
|
|
12
|
+
if sys.version_info >= (3, 11):
|
|
13
|
+
import tomllib
|
|
14
|
+
else:
|
|
15
|
+
import tomli as tomllib
|
|
16
|
+
|
|
12
17
|
from labtasker.errors import ConfigError
|
|
13
18
|
from labtasker.local import LocalPaths, local_paths, require_local_capabilities
|
|
14
19
|
from labtasker.validation import RequestValidationError, invalid_config, validate_identifier
|
|
@@ -17,6 +17,7 @@ from labtasker.types import JSONValue
|
|
|
17
17
|
from labtasker.validation import RequestValidationError, validate_json_object
|
|
18
18
|
|
|
19
19
|
CompletionReporter = Callable[[dict[str, JSONValue]], bool]
|
|
20
|
+
ProgressReporter = Callable[[dict[str, JSONValue]], bool]
|
|
20
21
|
ContextKind = Literal["python", "command"]
|
|
21
22
|
|
|
22
23
|
|
|
@@ -128,12 +129,14 @@ class ExecutionContext:
|
|
|
128
129
|
journal: LocalRunJournal,
|
|
129
130
|
reporter: CompletionReporter,
|
|
130
131
|
control: RunControl | None,
|
|
132
|
+
progress_reporter: ProgressReporter | None = None,
|
|
131
133
|
) -> None:
|
|
132
134
|
self.info = info
|
|
133
135
|
self.kind = kind
|
|
134
136
|
self.journal = journal
|
|
135
137
|
self.reporter = reporter
|
|
136
138
|
self.control = control
|
|
139
|
+
self.progress_reporter = progress_reporter
|
|
137
140
|
self._lock = threading.Lock()
|
|
138
141
|
self._finish_started = False
|
|
139
142
|
self._finished = False
|
|
@@ -161,6 +164,17 @@ class ExecutionContext:
|
|
|
161
164
|
self.control.complete()
|
|
162
165
|
_best_effort_journal(self.journal.acknowledged)
|
|
163
166
|
|
|
167
|
+
def report_progress(self, progress: dict[str, JSONValue]) -> bool:
|
|
168
|
+
with self._lock:
|
|
169
|
+
if self._finished:
|
|
170
|
+
raise RuntimeError("The current run has already completed.")
|
|
171
|
+
if self.control is not None and not self.control.active:
|
|
172
|
+
return False
|
|
173
|
+
reporter = self.progress_reporter
|
|
174
|
+
if reporter is None:
|
|
175
|
+
raise RuntimeError("Progress reporting is unavailable for this execution.")
|
|
176
|
+
return reporter(progress)
|
|
177
|
+
|
|
164
178
|
|
|
165
179
|
_CONTEXT_LOCK = threading.RLock()
|
|
166
180
|
_ACTIVE_CONTEXT: ExecutionContext | None = None
|
|
@@ -212,6 +226,20 @@ def finish(
|
|
|
212
226
|
context.finish(normalized)
|
|
213
227
|
|
|
214
228
|
|
|
229
|
+
def report_progress(
|
|
230
|
+
progress: dict[str, JSONValue],
|
|
231
|
+
*,
|
|
232
|
+
skip_if_no_labtasker: bool = False,
|
|
233
|
+
) -> bool:
|
|
234
|
+
context = _get_context()
|
|
235
|
+
if context is None:
|
|
236
|
+
if skip_if_no_labtasker:
|
|
237
|
+
return False
|
|
238
|
+
raise RuntimeError("No active Labtasker Task execution is available.")
|
|
239
|
+
normalized = validate_json_object(progress, field="progress")
|
|
240
|
+
return context.report_progress(normalized)
|
|
241
|
+
|
|
242
|
+
|
|
215
243
|
def cancellation_requested() -> bool:
|
|
216
244
|
context = _require_python_context()
|
|
217
245
|
if context.finished or context.control is None:
|
|
@@ -340,12 +368,24 @@ def _load_environment_context() -> ExecutionContext | None:
|
|
|
340
368
|
result=result,
|
|
341
369
|
)
|
|
342
370
|
|
|
371
|
+
def progress_reporter(progress: dict[str, JSONValue]) -> bool:
|
|
372
|
+
from labtasker.worker import report_progress_once
|
|
373
|
+
|
|
374
|
+
return report_progress_once(
|
|
375
|
+
client,
|
|
376
|
+
queue=values["queue"] or "",
|
|
377
|
+
task_id=values["task_id"] or "",
|
|
378
|
+
run_id=run_id,
|
|
379
|
+
progress=progress,
|
|
380
|
+
)
|
|
381
|
+
|
|
343
382
|
_ENV_CONTEXT = ExecutionContext(
|
|
344
383
|
info=info,
|
|
345
384
|
kind="command",
|
|
346
385
|
journal=journal,
|
|
347
386
|
reporter=report,
|
|
348
387
|
control=None,
|
|
388
|
+
progress_reporter=progress_reporter,
|
|
349
389
|
)
|
|
350
390
|
return _ENV_CONTEXT
|
|
351
391
|
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import tempfile
|
|
6
6
|
import threading
|
|
7
|
-
from datetime import
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Literal, TypedDict, cast
|
|
10
10
|
|
|
@@ -18,6 +18,8 @@ from labtasker.validation import (
|
|
|
18
18
|
validate_task_id,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
UTC = timezone.utc
|
|
22
|
+
|
|
21
23
|
JournalPhase = Literal["running", "reporting", "acknowledged", "revoked"]
|
|
22
24
|
TerminalAction = Literal["complete", "fail", "unclaim"]
|
|
23
25
|
LOCAL_GITIGNORE = "*\n!.gitignore\n"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from datetime import
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Literal
|
|
6
6
|
|
|
@@ -18,6 +18,8 @@ from labtasker.validation import (
|
|
|
18
18
|
validate_unicode_scalar,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
UTC = timezone.utc
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class ResponseModel(BaseModel):
|
|
23
25
|
model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
|
|
@@ -66,6 +68,9 @@ class Task(ResponseModel):
|
|
|
66
68
|
max_attempts: int
|
|
67
69
|
routes: list[str]
|
|
68
70
|
result: dict[str, JSONValue]
|
|
71
|
+
progress: dict[str, JSONValue] | None = None
|
|
72
|
+
progress_updated_at: datetime | None = None
|
|
73
|
+
progress_attempt: int | None = None
|
|
69
74
|
last_error: LastError | None
|
|
70
75
|
last_route: str | None
|
|
71
76
|
created_at: datetime
|
|
@@ -88,9 +93,15 @@ class Task(ResponseModel):
|
|
|
88
93
|
def validate_name(cls, value: str | None) -> str | None:
|
|
89
94
|
return validate_task_name(value)
|
|
90
95
|
|
|
91
|
-
@field_validator("args", "metadata", "result")
|
|
96
|
+
@field_validator("args", "metadata", "result", "progress")
|
|
92
97
|
@classmethod
|
|
93
|
-
def validate_objects(
|
|
98
|
+
def validate_objects(
|
|
99
|
+
cls,
|
|
100
|
+
value: dict[str, JSONValue] | None,
|
|
101
|
+
info: object,
|
|
102
|
+
) -> dict[str, JSONValue] | None:
|
|
103
|
+
if value is None:
|
|
104
|
+
return None
|
|
94
105
|
return validate_json_object(value, field=getattr(info, "field_name", "task"))
|
|
95
106
|
|
|
96
107
|
@field_validator("priority", "attempt")
|
|
@@ -98,6 +109,23 @@ class Task(ResponseModel):
|
|
|
98
109
|
def validate_numbers(cls, value: int, info: object) -> int:
|
|
99
110
|
return validate_int64(value, field=getattr(info, "field_name", "task"))
|
|
100
111
|
|
|
112
|
+
@field_validator("progress_attempt")
|
|
113
|
+
@classmethod
|
|
114
|
+
def validate_progress_attempt(cls, value: int | None) -> int | None:
|
|
115
|
+
if value is None:
|
|
116
|
+
return None
|
|
117
|
+
value = validate_int64(value, field="progress_attempt")
|
|
118
|
+
if value < 0:
|
|
119
|
+
raise ValueError("progress_attempt must be non-negative")
|
|
120
|
+
return value
|
|
121
|
+
|
|
122
|
+
@model_validator(mode="after")
|
|
123
|
+
def validate_progress_fields(self) -> Task:
|
|
124
|
+
fields = (self.progress, self.progress_updated_at, self.progress_attempt)
|
|
125
|
+
if any(value is None for value in fields) and any(value is not None for value in fields):
|
|
126
|
+
raise ValueError("progress fields must be all null or all present")
|
|
127
|
+
return self
|
|
128
|
+
|
|
101
129
|
@field_validator("max_attempts")
|
|
102
130
|
@classmethod
|
|
103
131
|
def validate_max_attempts(cls, value: int) -> int:
|
|
@@ -116,7 +144,13 @@ class Task(ResponseModel):
|
|
|
116
144
|
def validate_last_route(cls, value: str | None) -> str | None:
|
|
117
145
|
return None if value is None else validate_identifier(value, field="last_route")
|
|
118
146
|
|
|
119
|
-
@field_validator(
|
|
147
|
+
@field_validator(
|
|
148
|
+
"created_at",
|
|
149
|
+
"updated_at",
|
|
150
|
+
"started_at",
|
|
151
|
+
"finished_at",
|
|
152
|
+
"progress_updated_at",
|
|
153
|
+
)
|
|
120
154
|
@classmethod
|
|
121
155
|
def validate_times(cls, value: datetime | None) -> datetime | None:
|
|
122
156
|
return None if value is None else _utc_datetime(value)
|
|
@@ -19,6 +19,7 @@ from labtasker.errors import (
|
|
|
19
19
|
APIError,
|
|
20
20
|
ConfigError,
|
|
21
21
|
FatalWorkerError,
|
|
22
|
+
LabtaskerError,
|
|
22
23
|
TransientError,
|
|
23
24
|
TransportError,
|
|
24
25
|
)
|
|
@@ -260,6 +261,16 @@ def _run_python_claim(
|
|
|
260
261
|
control.revoke("stale_run")
|
|
261
262
|
return accepted
|
|
262
263
|
|
|
264
|
+
def progress_reporter(progress: dict[str, JSONValue]) -> bool:
|
|
265
|
+
return report_progress_once(
|
|
266
|
+
client,
|
|
267
|
+
queue=queue,
|
|
268
|
+
task_id=claim.task.id,
|
|
269
|
+
run_id=claim.run_id,
|
|
270
|
+
progress=progress,
|
|
271
|
+
control=control,
|
|
272
|
+
)
|
|
273
|
+
|
|
263
274
|
info = TaskInfo(
|
|
264
275
|
**claim.task.model_dump(),
|
|
265
276
|
run_id=claim.run_id,
|
|
@@ -271,6 +282,7 @@ def _run_python_claim(
|
|
|
271
282
|
journal=journal,
|
|
272
283
|
reporter=report_complete,
|
|
273
284
|
control=control,
|
|
285
|
+
progress_reporter=progress_reporter,
|
|
274
286
|
)
|
|
275
287
|
heartbeat = Heartbeat(
|
|
276
288
|
client,
|
|
@@ -358,6 +370,39 @@ def report_complete_until_resolved(
|
|
|
358
370
|
)
|
|
359
371
|
|
|
360
372
|
|
|
373
|
+
def report_progress_once(
|
|
374
|
+
client: Client,
|
|
375
|
+
*,
|
|
376
|
+
queue: str,
|
|
377
|
+
task_id: str,
|
|
378
|
+
run_id: str,
|
|
379
|
+
progress: dict[str, JSONValue],
|
|
380
|
+
control: RunControl | None = None,
|
|
381
|
+
) -> bool:
|
|
382
|
+
try:
|
|
383
|
+
client._report_progress(
|
|
384
|
+
task_id=task_id,
|
|
385
|
+
run_id=run_id,
|
|
386
|
+
progress=progress,
|
|
387
|
+
queue=queue,
|
|
388
|
+
)
|
|
389
|
+
except APIError as error:
|
|
390
|
+
if error.code == "run_finalized" and error.details.get("action") == "complete":
|
|
391
|
+
if control is not None:
|
|
392
|
+
control.complete()
|
|
393
|
+
elif error.code in {"run_finalized", "stale_run"} and control is not None:
|
|
394
|
+
control.revoke(str(error.details.get("action", error.code)))
|
|
395
|
+
logger.warning("Progress report rejected; continuing Task: %s", error.message)
|
|
396
|
+
return False
|
|
397
|
+
except TransportError as error:
|
|
398
|
+
logger.warning("Progress report transport error; continuing Task: %s", error.message)
|
|
399
|
+
return False
|
|
400
|
+
except LabtaskerError as error:
|
|
401
|
+
logger.warning("Progress report failed; continuing Task: %s", error.message)
|
|
402
|
+
return False
|
|
403
|
+
return True
|
|
404
|
+
|
|
405
|
+
|
|
361
406
|
def _report_complete(
|
|
362
407
|
client: Client,
|
|
363
408
|
journal: LocalRunJournal,
|
labtasker_client-2.2.0/PKG-INFO
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: labtasker-client
|
|
3
|
-
Version: 2.2.0
|
|
4
|
-
Summary: A small task queue for parallel model inference and evaluation
|
|
5
|
-
Project-URL: Homepage, https://github.com/luocfprime/labtasker
|
|
6
|
-
Project-URL: Repository, https://github.com/luocfprime/labtasker.git
|
|
7
|
-
Author-email: lcf <luocfprime@gmail.com>
|
|
8
|
-
License-Expression: Apache-2.0
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Requires-Python: >=3.11
|
|
11
|
-
Requires-Dist: httpx[socks]<1,>=0.28
|
|
12
|
-
Requires-Dist: packaging<27,>=24
|
|
13
|
-
Requires-Dist: pydantic<3,>=2.13.5
|
|
14
|
-
Requires-Dist: typer<1,>=0.16
|
|
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
|