gridlinegpu 0.1.3__tar.gz → 0.1.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gridlinegpu
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Python client for Gridline YAML workload routing
5
5
  Author: Gridline GPU
6
6
  License: Proprietary
@@ -23,7 +23,7 @@ the `gridline` command in the same Python environment on macOS and Linux.
23
23
  ## Install
24
24
 
25
25
  ```sh
26
- python -m pip install 'gridlinegpu>=0.1.3,<0.2'
26
+ python -m pip install 'gridlinegpu>=0.1.4,<0.2'
27
27
  ```
28
28
 
29
29
  Run browser setup once. It creates a 30-day scoped token in a private file, attempts to
@@ -36,12 +36,16 @@ gridline setup --apply
36
36
 
37
37
  Use a YAML file beside its referenced inputs. Task, model, engine, providers, output,
38
38
  budget, and deadline belong in YAML rather than Python arguments.
39
- Current hosted artifact limits are 40 MiB per local input and 8 MiB per output.
39
+ Server-advertised artifact limits govern each submission. Newer Gridline deployments
40
+ use direct, signed resumable Storage uploads for large local inputs; older deployments
41
+ may still cap inputs at 40 MiB and outputs at 8 MiB.
40
42
 
41
43
  ```python
42
44
  from gridlinegpu import GridlineClient
43
45
 
44
46
  client = GridlineClient()
47
+ search = client.search("workload.yaml")
48
+ print(search["coverage"], search["offers"]) # Catalog facts may be unknown; coverage may be partial.
45
49
  plan = client.plan("workload.yaml")
46
50
  print(plan.review) # Check selected GPU, exclusions, estimate, and maximum.
47
51
 
@@ -56,6 +60,7 @@ print(client.outputs(job_id, "results"))
56
60
  The same installed package supports a terminal flow:
57
61
 
58
62
  ```sh
63
+ gridline workload search workload.yaml
59
64
  gridline workload validate --file workload.yaml
60
65
  gridline workload plan --file workload.yaml
61
66
  gridline workload run --file workload.yaml --apply --max-cost-usd 5 \
@@ -79,6 +84,13 @@ approval; the Python CLI never stores a privileged refresh token.
79
84
  After revoking a token, remove its local file before running setup again; setup never
80
85
  overwrites an existing file.
81
86
 
87
+ `plan()` validates YAML and checks advertised task readiness before reserving or
88
+ uploading inputs. An explicitly unavailable managed ASR/LLM runtime, minimal managed
89
+ model resolver, or custom finite path raises `GridlineError` with code
90
+ `planning_unavailable`. Qualify the custom container and enable its launch path, or
91
+ use available managed runtime/full YAML settings as indicated by the error. Missing
92
+ task readiness fields on older servers preserve existing behavior. Custom endpoint
93
+ support remains a server planning decision; finite readiness does not describe it.
82
94
  `plan()` stages input bytes but does not allocate a GPU. `launch()` never silently
83
95
  changes a plan or retries an ambiguous paid request. If launch outcome is unknown,
84
96
  inspect `LaunchOutcomeUnknown.workload_id` with the same idempotency key; do not use a
@@ -86,3 +98,32 @@ new key. `outputs()` verifies byte count and SHA-256 before creating private fil
86
98
  Cancellation requests stop; cleanup and final provider billing can remain pending.
87
99
  For finite jobs, `wait()` returns when outputs are complete, even if cleanup and billing
88
100
  leave `outcome` pending. Check `status()` later for final settlement.
101
+
102
+ `search()` reads only YAML and connected-provider catalogs. It does not inspect or
103
+ upload referenced inputs, resolve a model or image, create a workload, or allocate a
104
+ GPU. `availability()["searchAvailable"]` is separate from planning and execution
105
+ readiness. Missing price, VRAM, capacity, or topology facts remain unknown. Partial
106
+ catalog coverage and unsupported execution remain visible; a search result is not a
107
+ launch quote. `scopeStatus: "empty_scope"` with `catalogCompleteness: "not_applicable"`
108
+ means no accounts were searched. Servers with catalog search disabled return a
109
+ search availability error. The CLI prints the same JSON response and also accepts
110
+ `gridline workload search --file workload.yaml`.
111
+
112
+ When worker progress is enabled on the hosted API/control service and supported by the
113
+ deployed worker image, `status()` also returns optional `progress`:
114
+
115
+ ```python
116
+ status = client.status(job_id)
117
+ progress = status.get("progress")
118
+ if progress:
119
+ for event in progress["events"]:
120
+ print(event["stage"], event["observedAt"])
121
+ ```
122
+
123
+ Events are bounded, ordered worker reports for `progress["attemptId"]` and its fence.
124
+ Stages include input staging, model preparation/loading, inference start/completion,
125
+ and output upload. `observedAt` is the server's receipt time. Poll no faster than every
126
+ five seconds; deduplicate by attempt/fence/stage. Missing progress or skipped stages
127
+ mean unavailable evidence, not failure or an inferred stage. Inference completion
128
+ does not imply stored outputs, job success, cleanup, or settled billing. `wait()` keeps
129
+ its existing completion behavior and does not stream progress callbacks.
@@ -8,7 +8,7 @@ the `gridline` command in the same Python environment on macOS and Linux.
8
8
  ## Install
9
9
 
10
10
  ```sh
11
- python -m pip install 'gridlinegpu>=0.1.3,<0.2'
11
+ python -m pip install 'gridlinegpu>=0.1.4,<0.2'
12
12
  ```
13
13
 
14
14
  Run browser setup once. It creates a 30-day scoped token in a private file, attempts to
@@ -21,12 +21,16 @@ gridline setup --apply
21
21
 
22
22
  Use a YAML file beside its referenced inputs. Task, model, engine, providers, output,
23
23
  budget, and deadline belong in YAML rather than Python arguments.
24
- Current hosted artifact limits are 40 MiB per local input and 8 MiB per output.
24
+ Server-advertised artifact limits govern each submission. Newer Gridline deployments
25
+ use direct, signed resumable Storage uploads for large local inputs; older deployments
26
+ may still cap inputs at 40 MiB and outputs at 8 MiB.
25
27
 
26
28
  ```python
27
29
  from gridlinegpu import GridlineClient
28
30
 
29
31
  client = GridlineClient()
32
+ search = client.search("workload.yaml")
33
+ print(search["coverage"], search["offers"]) # Catalog facts may be unknown; coverage may be partial.
30
34
  plan = client.plan("workload.yaml")
31
35
  print(plan.review) # Check selected GPU, exclusions, estimate, and maximum.
32
36
 
@@ -41,6 +45,7 @@ print(client.outputs(job_id, "results"))
41
45
  The same installed package supports a terminal flow:
42
46
 
43
47
  ```sh
48
+ gridline workload search workload.yaml
44
49
  gridline workload validate --file workload.yaml
45
50
  gridline workload plan --file workload.yaml
46
51
  gridline workload run --file workload.yaml --apply --max-cost-usd 5 \
@@ -64,6 +69,13 @@ approval; the Python CLI never stores a privileged refresh token.
64
69
  After revoking a token, remove its local file before running setup again; setup never
65
70
  overwrites an existing file.
66
71
 
72
+ `plan()` validates YAML and checks advertised task readiness before reserving or
73
+ uploading inputs. An explicitly unavailable managed ASR/LLM runtime, minimal managed
74
+ model resolver, or custom finite path raises `GridlineError` with code
75
+ `planning_unavailable`. Qualify the custom container and enable its launch path, or
76
+ use available managed runtime/full YAML settings as indicated by the error. Missing
77
+ task readiness fields on older servers preserve existing behavior. Custom endpoint
78
+ support remains a server planning decision; finite readiness does not describe it.
67
79
  `plan()` stages input bytes but does not allocate a GPU. `launch()` never silently
68
80
  changes a plan or retries an ambiguous paid request. If launch outcome is unknown,
69
81
  inspect `LaunchOutcomeUnknown.workload_id` with the same idempotency key; do not use a
@@ -71,3 +83,32 @@ new key. `outputs()` verifies byte count and SHA-256 before creating private fil
71
83
  Cancellation requests stop; cleanup and final provider billing can remain pending.
72
84
  For finite jobs, `wait()` returns when outputs are complete, even if cleanup and billing
73
85
  leave `outcome` pending. Check `status()` later for final settlement.
86
+
87
+ `search()` reads only YAML and connected-provider catalogs. It does not inspect or
88
+ upload referenced inputs, resolve a model or image, create a workload, or allocate a
89
+ GPU. `availability()["searchAvailable"]` is separate from planning and execution
90
+ readiness. Missing price, VRAM, capacity, or topology facts remain unknown. Partial
91
+ catalog coverage and unsupported execution remain visible; a search result is not a
92
+ launch quote. `scopeStatus: "empty_scope"` with `catalogCompleteness: "not_applicable"`
93
+ means no accounts were searched. Servers with catalog search disabled return a
94
+ search availability error. The CLI prints the same JSON response and also accepts
95
+ `gridline workload search --file workload.yaml`.
96
+
97
+ When worker progress is enabled on the hosted API/control service and supported by the
98
+ deployed worker image, `status()` also returns optional `progress`:
99
+
100
+ ```python
101
+ status = client.status(job_id)
102
+ progress = status.get("progress")
103
+ if progress:
104
+ for event in progress["events"]:
105
+ print(event["stage"], event["observedAt"])
106
+ ```
107
+
108
+ Events are bounded, ordered worker reports for `progress["attemptId"]` and its fence.
109
+ Stages include input staging, model preparation/loading, inference start/completion,
110
+ and output upload. `observedAt` is the server's receipt time. Poll no faster than every
111
+ five seconds; deduplicate by attempt/fence/stage. Missing progress or skipped stages
112
+ mean unavailable evidence, not failure or an inferred stage. Inference completion
113
+ does not imply stored outputs, job success, cleanup, or settled billing. `wait()` keeps
114
+ its existing completion behavior and does not stream progress callbacks.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gridlinegpu"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  description = "Python client for Gridline YAML workload routing"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -49,8 +49,11 @@ def _parser() -> argparse.ArgumentParser:
49
49
  revoke.add_argument("token_id")
50
50
  revoke.add_argument("--apply", action="store_true")
51
51
 
52
- workload = commands.add_parser("workload", help="Validate, plan, launch, and inspect YAML jobs")
52
+ workload = commands.add_parser("workload", help="Search catalogs, plan, launch, and inspect YAML jobs")
53
53
  actions = workload.add_subparsers(dest="action", required=True)
54
+ search = actions.add_parser("search", help="Search connected-provider catalogs without staging inputs")
55
+ search.add_argument("yaml_path", type=Path, nargs="?")
56
+ search.add_argument("--file", type=Path)
54
57
  for name, help_text in [
55
58
  ("validate", "Validate YAML without allocating a GPU"),
56
59
  ("plan", "Stage inputs and review a route without allocating a GPU"),
@@ -77,7 +80,7 @@ def _parser() -> argparse.ArgumentParser:
77
80
  if name == "cancel":
78
81
  action.add_argument("--apply", action="store_true")
79
82
  action.add_argument("--yes", action="store_true")
80
- actions.add_parser("availability", help="Show account planning and execution gates")
83
+ actions.add_parser("availability", help="Show account search, planning, and execution gates")
81
84
  return parser
82
85
 
83
86
 
@@ -85,6 +88,15 @@ def _emit(value: Any, output: TextIO) -> None:
85
88
  print(json.dumps(value, indent=2, sort_keys=True, default=str), file=output)
86
89
 
87
90
 
91
+ def _warn_unmeasured_duration(review: dict[str, Any], errors: TextIO) -> None:
92
+ quote = review.get("result", {}).get("quote", {})
93
+ selected = quote.get("selected", {}) if isinstance(quote, dict) else {}
94
+ duration = selected.get("expectedDurationMs", {}) if isinstance(selected, dict) else {}
95
+ if isinstance(duration, dict) and duration.get("source") == "customer_declared":
96
+ print("Gridline: task-cost ranking uses customer-declared duration, not measured "
97
+ "GPU-specific performance; estimate is not guaranteed.", file=errors)
98
+
99
+
88
100
  def _confirm(message: str, *, yes: bool, input_stream: TextIO,
89
101
  output: TextIO) -> None:
90
102
  if yes:
@@ -136,10 +148,15 @@ def main(argv: Sequence[str] | None = None, *, input_stream: TextIO = sys.stdin,
136
148
  action = args.action
137
149
  if action == "availability":
138
150
  result = client.availability()
151
+ elif action == "search":
152
+ if (args.yaml_path is None) == (args.file is None):
153
+ raise ValueError("workload search requires YAML or --file YAML, exactly once")
154
+ result = client.search(args.yaml_path or args.file)
139
155
  elif action == "validate":
140
156
  result = client.validate(args.file)
141
157
  elif action == "plan":
142
158
  result = client.plan(args.file).review
159
+ _warn_unmeasured_duration(result, errors)
143
160
  elif action == "run":
144
161
  if not args.apply:
145
162
  raise GridlineError("Paid launch requires --apply", code="apply_required")
@@ -151,6 +168,7 @@ def main(argv: Sequence[str] | None = None, *, input_stream: TextIO = sys.stdin,
151
168
  raise ValueError("--idempotency-key must contain 1–128 safe characters")
152
169
  planned = client.plan(args.file)
153
170
  _emit(planned.review, output)
171
+ _warn_unmeasured_duration(planned.review, errors)
154
172
  if planned.quote is None:
155
173
  raise GridlineError("Router abstained; inspect plan coverage", code="plan_abstained")
156
174
  _confirm(f"Launch with approved ceiling ${amount}?", yes=args.yes,