uenv-bridge 0.2.1__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.
- uenv/bridge/__init__.py +4 -0
- uenv/bridge/authorized_hub.py +43 -0
- uenv/bridge/cli.py +294 -0
- uenv/bridge/client.py +746 -0
- uenv/bridge/client_identity.py +109 -0
- uenv/bridge/client_settings.py +79 -0
- uenv/bridge/commands.py +473 -0
- uenv/bridge/data_output.py +89 -0
- uenv/bridge/evaluation.py +223 -0
- uenv/bridge/gateway_hub.py +61 -0
- uenv/bridge/gateway_training.py +50 -0
- uenv/bridge/integrations/__init__.py +1 -0
- uenv/bridge/integrations/verl/__init__.py +1 -0
- uenv/bridge/integrations/verl/adapter.py +111 -0
- uenv/bridge/integrations/verl/agent_loop.py +203 -0
- uenv/bridge/integrations/verl/compat.json +97 -0
- uenv/bridge/integrations/verl/configuration.py +65 -0
- uenv/bridge/integrations/verl/dataset.py +44 -0
- uenv/bridge/integrations/verl/jobs.py +553 -0
- uenv/bridge/integrations/verl/local.py +189 -0
- uenv/bridge/integrations/verl/recipes/agent-loop.yaml +3 -0
- uenv/bridge/integrations/verl/recipes/verl_async.yaml +97 -0
- uenv/bridge/integrations/verl/recipes/verl_sync.yaml +104 -0
- uenv/bridge/integrations/verl/sampling.py +236 -0
- uenv/bridge/integrations/verl/setup.py +59 -0
- uenv/bridge/management_client.py +80 -0
- uenv/bridge/management_gateway.py +244 -0
- uenv/bridge/management_migration.py +45 -0
- uenv/bridge/model_gateway.py +168 -0
- uenv/bridge/model_sessions.py +137 -0
- uenv/bridge/platform_identity.py +286 -0
- uenv/bridge/platform_quota.py +59 -0
- uenv/bridge/platform_resources.py +154 -0
- uenv/bridge/result_export.py +162 -0
- uenv/bridge/result_reader.py +105 -0
- uenv/bridge/run_configuration.py +50 -0
- uenv/bridge/run_management.py +68 -0
- uenv/bridge/training.py +208 -0
- uenv/bridge/training_data.py +39 -0
- uenv/bridge/training_management.py +111 -0
- uenv/bridge/web.py +1711 -0
- uenv/bridge/web_access.py +72 -0
- uenv/bridge/web_assets/app.js +79 -0
- uenv/bridge/web_assets/index.html +14 -0
- uenv/bridge/web_assets/style.css +1 -0
- uenv/bridge/web_github_login.py +142 -0
- uenv/bridge/web_hub_access.py +54 -0
- uenv/bridge/web_identity.py +115 -0
- uenv/bridge/web_local_training.py +258 -0
- uenv/bridge/web_model_sessions.py +212 -0
- uenv/bridge/web_publication.py +277 -0
- uenv/bridge/web_state.py +571 -0
- uenv/bridge/web_training_routing.py +121 -0
- uenv/bridge/wire.py +55 -0
- uenv_bridge-0.2.1.dist-info/METADATA +16 -0
- uenv_bridge-0.2.1.dist-info/RECORD +59 -0
- uenv_bridge-0.2.1.dist-info/WHEEL +5 -0
- uenv_bridge-0.2.1.dist-info/entry_points.txt +5 -0
- uenv_bridge-0.2.1.dist-info/top_level.txt +1 -0
uenv/bridge/__init__.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Authorize existing Hub reads before the trusted preparation pipeline uses them."""
|
|
2
|
+
from uenv.sdk.hub import HubClient
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AuthorizedHub(HubClient):
|
|
6
|
+
def __init__(self, hub, policy, principal, *, permission='execute'):
|
|
7
|
+
super().__init__(hub.origin.geturl())
|
|
8
|
+
self.hub, self.policy, self.principal, self.permission = hub, policy, principal, permission
|
|
9
|
+
self.origin, self.cache = hub.origin, None
|
|
10
|
+
|
|
11
|
+
def require(self, kind, ref):
|
|
12
|
+
return self.policy.require(self.principal, kind, ref['id'],
|
|
13
|
+
ref['revision' if kind == 'data' else 'version'], permission=self.permission)
|
|
14
|
+
|
|
15
|
+
def package(self, ref):
|
|
16
|
+
self.require('package',ref)
|
|
17
|
+
return self.hub.package(ref)
|
|
18
|
+
|
|
19
|
+
def data(self, ref):
|
|
20
|
+
self.require('data',ref)
|
|
21
|
+
result = self.hub.data(ref)
|
|
22
|
+
self.require('package',result['dataset_package'])
|
|
23
|
+
return result
|
|
24
|
+
|
|
25
|
+
def dataset_release(self, ref):
|
|
26
|
+
self.data(ref)
|
|
27
|
+
return self.hub.dataset_release(ref)
|
|
28
|
+
|
|
29
|
+
def samples(self, dataset, **kwargs):
|
|
30
|
+
self.data(dataset)
|
|
31
|
+
return self.hub.samples(dataset, **kwargs)
|
|
32
|
+
|
|
33
|
+
def iter_samples(self, dataset, **kwargs):
|
|
34
|
+
self.data(dataset)
|
|
35
|
+
return self.hub.iter_samples(dataset, **kwargs)
|
|
36
|
+
|
|
37
|
+
def _cached_file(self, query):
|
|
38
|
+
self.require('package' if 'package' in query else 'data', query.get('package',query.get('dataset')))
|
|
39
|
+
return self.hub._cached_file(query)
|
|
40
|
+
|
|
41
|
+
def cached_data_file(self, manifest, ref):
|
|
42
|
+
self.data(manifest['dataset'])
|
|
43
|
+
return self.hub.cached_data_file(manifest,ref)
|
uenv/bridge/cli.py
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""Load files, then call the same UEnvClient methods as Python integrations."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from uenv.sdk import SchemaRegistry
|
|
9
|
+
from uenv.sdk.package_loader import load_yaml
|
|
10
|
+
from .client import UEnvClient
|
|
11
|
+
from .client_settings import load_client_settings
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main(argv=None):
|
|
15
|
+
parser = argparse.ArgumentParser(description="Submit and manage unified UEnv runs")
|
|
16
|
+
parser.add_argument("--config", type=Path, help="Client connection YAML")
|
|
17
|
+
parser.add_argument("--endpoint")
|
|
18
|
+
parser.add_argument("--direct", action="store_true", help="Explicit legacy caller-owned submission; --wait owns its window")
|
|
19
|
+
action = parser.add_mutually_exclusive_group(required=True)
|
|
20
|
+
action.add_argument("--run", type=Path)
|
|
21
|
+
action.add_argument("--retry-submission", metavar="SUBMISSION_ID", help="Retry the saved request without replacing its configuration")
|
|
22
|
+
action.add_argument(
|
|
23
|
+
"--status", metavar="RUN_ID", help="Read current run state counts"
|
|
24
|
+
)
|
|
25
|
+
action.add_argument(
|
|
26
|
+
"--cancel-run",
|
|
27
|
+
metavar="RUN_ID",
|
|
28
|
+
help="Cancel the run and reject later new batches",
|
|
29
|
+
)
|
|
30
|
+
action.add_argument(
|
|
31
|
+
"--upload-file",
|
|
32
|
+
type=Path,
|
|
33
|
+
help="Upload task material using the same UEnvClient.upload_file API",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--media-type",
|
|
37
|
+
help="Uploaded file media type; default application/octet-stream",
|
|
38
|
+
)
|
|
39
|
+
source = parser.add_mutually_exclusive_group()
|
|
40
|
+
source.add_argument("--samples", type=Path)
|
|
41
|
+
source.add_argument(
|
|
42
|
+
"--hub-selection",
|
|
43
|
+
type=Path,
|
|
44
|
+
help="Deprecated: HubSampleQuery file; put dataset in --run instead",
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--hub-endpoint", help="Hub origin"
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--raw-package",
|
|
51
|
+
type=Path,
|
|
52
|
+
help="Trusted selected dataset package; read and normalize the package source format before submission",
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--dataset",
|
|
56
|
+
type=Path,
|
|
57
|
+
help="Deprecated: data identity file for --raw-package; put dataset in --run instead",
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument("--wait", action="store_true")
|
|
60
|
+
parser.add_argument("--output", type=Path, help="Write unchanged EpisodeResult JSONL; requires --wait")
|
|
61
|
+
parser.add_argument("--batch-size", type=int, help="Maximum submitted episodes awaiting results")
|
|
62
|
+
parser.add_argument("--name", default="", help="Display name for a managed run")
|
|
63
|
+
parser.add_argument("--id", help="Stable idempotency key for a managed Hub run; reuse after a lost HTTP reply")
|
|
64
|
+
args = parser.parse_args(argv)
|
|
65
|
+
if args.name and (not args.run or args.direct):
|
|
66
|
+
parser.error("--name requires a managed --run")
|
|
67
|
+
if len(args.name) > 200:
|
|
68
|
+
parser.error("--name must be at most 200 characters")
|
|
69
|
+
if args.id and (not args.run or args.direct):
|
|
70
|
+
parser.error("--id requires a managed --run")
|
|
71
|
+
if args.output is not None and not ((args.run or args.retry_submission) and args.wait):
|
|
72
|
+
parser.error("--output requires --run and --wait")
|
|
73
|
+
if args.batch_size is not None and (not args.run or (args.direct and not args.wait)):
|
|
74
|
+
parser.error("--batch-size requires --run; --direct also requires --wait")
|
|
75
|
+
if args.batch_size is not None and args.batch_size < 1:
|
|
76
|
+
parser.error("--batch-size must be positive")
|
|
77
|
+
try:
|
|
78
|
+
connection = load_client_settings(args.config, server=args.endpoint, hub=args.hub_endpoint,
|
|
79
|
+
require_hub=bool(args.run), allow_platform=bool((args.status or args.cancel_run or args.run) and not args.direct))
|
|
80
|
+
except ValueError as error:
|
|
81
|
+
parser.error(str(error))
|
|
82
|
+
args.endpoint = connection.server
|
|
83
|
+
if args.run and not args.direct and not connection.management:
|
|
84
|
+
parser.error("Managed runs require management in client.yaml; --direct explicitly selects caller-owned submission")
|
|
85
|
+
def observe_operation(manager, operation, *, stop_event=None):
|
|
86
|
+
print(json.dumps(operation, ensure_ascii=False), file=sys.stderr if args.wait else sys.stdout, flush=True)
|
|
87
|
+
if args.wait:
|
|
88
|
+
from .evaluation import observe_run
|
|
89
|
+
run_id = manager.wait_submission(operation,stop_event=stop_event)
|
|
90
|
+
summary = observe_run(manager, run_id, output=args.output,stop_event=stop_event)
|
|
91
|
+
print(json.dumps(summary, ensure_ascii=False), file=sys.stderr)
|
|
92
|
+
return summary["exit_code"]
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
if args.retry_submission:
|
|
96
|
+
if args.direct or not connection.management or any((args.samples, args.hub_selection, args.raw_package, args.dataset, args.media_type, args.hub_endpoint)):
|
|
97
|
+
parser.error("--retry-submission requires management and takes no replacement task or execution parameters")
|
|
98
|
+
from .management_gateway import ManagementGatewayClient
|
|
99
|
+
manager = ManagementGatewayClient(connection.management, token_file=connection.management_token_file)
|
|
100
|
+
return observe_operation(manager, manager.submission(args.retry_submission, retry=True))
|
|
101
|
+
if args.status is not None or args.cancel_run is not None:
|
|
102
|
+
if any(
|
|
103
|
+
(
|
|
104
|
+
args.samples,
|
|
105
|
+
args.hub_selection,
|
|
106
|
+
args.hub_endpoint,
|
|
107
|
+
args.raw_package,
|
|
108
|
+
args.dataset,
|
|
109
|
+
args.wait,
|
|
110
|
+
args.media_type,
|
|
111
|
+
)
|
|
112
|
+
):
|
|
113
|
+
parser.error(
|
|
114
|
+
"Run management cannot be combined with submission or upload arguments"
|
|
115
|
+
)
|
|
116
|
+
if connection.management and not args.direct:
|
|
117
|
+
from .management_gateway import ManagementGatewayClient
|
|
118
|
+
manager = ManagementGatewayClient(connection.management, token_file=connection.management_token_file)
|
|
119
|
+
result = manager.run_status(args.status) if args.status is not None else manager.management.stop("run", args.cancel_run)
|
|
120
|
+
print(json.dumps(result, ensure_ascii=False))
|
|
121
|
+
return
|
|
122
|
+
with UEnvClient(args.endpoint) as client:
|
|
123
|
+
result = (
|
|
124
|
+
client.run_status(args.status)
|
|
125
|
+
if args.status is not None
|
|
126
|
+
else (client.cancel_run(args.cancel_run) if args.direct
|
|
127
|
+
else client.management.stop("run", args.cancel_run))
|
|
128
|
+
)
|
|
129
|
+
print(json.dumps(result, ensure_ascii=False))
|
|
130
|
+
return
|
|
131
|
+
if args.upload_file:
|
|
132
|
+
if any(
|
|
133
|
+
(
|
|
134
|
+
args.samples,
|
|
135
|
+
args.hub_selection,
|
|
136
|
+
args.hub_endpoint,
|
|
137
|
+
args.raw_package,
|
|
138
|
+
args.dataset,
|
|
139
|
+
args.wait,
|
|
140
|
+
)
|
|
141
|
+
):
|
|
142
|
+
parser.error(
|
|
143
|
+
"--upload-file cannot be combined with task submission arguments"
|
|
144
|
+
)
|
|
145
|
+
with UEnvClient(args.endpoint) as client:
|
|
146
|
+
print(
|
|
147
|
+
json.dumps(
|
|
148
|
+
client.upload_file(
|
|
149
|
+
args.upload_file,
|
|
150
|
+
media_type=args.media_type or "application/octet-stream",
|
|
151
|
+
),
|
|
152
|
+
ensure_ascii=False,
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
return
|
|
156
|
+
if args.media_type:
|
|
157
|
+
parser.error("--media-type is only valid with --upload-file")
|
|
158
|
+
run = load_yaml(args.run)
|
|
159
|
+
inline_dataset = isinstance(run, dict) and "dataset" in run
|
|
160
|
+
if args.hub_selection and inline_dataset:
|
|
161
|
+
parser.error("--hub-selection conflicts with dataset in --run")
|
|
162
|
+
if args.dataset and inline_dataset:
|
|
163
|
+
parser.error("--dataset conflicts with dataset in --run")
|
|
164
|
+
if args.dataset and not args.raw_package:
|
|
165
|
+
parser.error("--dataset requires --raw-package")
|
|
166
|
+
if args.raw_package and not (args.dataset or inline_dataset):
|
|
167
|
+
parser.error("--raw-package requires dataset in --run (or legacy --dataset)")
|
|
168
|
+
if args.hub_selection and args.raw_package:
|
|
169
|
+
parser.error("Hub rows are already normalized; --raw-package is not applicable")
|
|
170
|
+
embedded_hub = inline_dataset and not args.raw_package
|
|
171
|
+
if embedded_hub and args.samples:
|
|
172
|
+
parser.error("dataset in --run cannot be combined with --samples unless --raw-package is used")
|
|
173
|
+
if not args.direct and embedded_hub:
|
|
174
|
+
from uuid import uuid4
|
|
175
|
+
from .management_gateway import ManagementGatewayClient
|
|
176
|
+
identifier = args.id or str(uuid4())
|
|
177
|
+
# Print the retry key before HTTP, including when its acknowledgement is lost.
|
|
178
|
+
print(json.dumps({"submission_key": identifier}), file=sys.stderr, flush=True)
|
|
179
|
+
manager = ManagementGatewayClient(connection.management, token_file=connection.management_token_file)
|
|
180
|
+
if connection.platform:
|
|
181
|
+
if not args.wait:
|
|
182
|
+
parser.error("Platform local models require --wait to keep the outbound model session alive")
|
|
183
|
+
if run.get("model",{}).get("credential_ref"):
|
|
184
|
+
parser.error("The local model session does not use platform credential references")
|
|
185
|
+
import asyncio
|
|
186
|
+
from .model_sessions import OutboundModelSession
|
|
187
|
+
async def connected():
|
|
188
|
+
import threading
|
|
189
|
+
stop = threading.Event()
|
|
190
|
+
operation = None
|
|
191
|
+
async with OutboundModelSession(manager,run["model"]["endpoint"]) as session:
|
|
192
|
+
try:
|
|
193
|
+
operation = await asyncio.to_thread(manager.submit_run,run,identifier=identifier,
|
|
194
|
+
name=args.name,batch_size=args.batch_size or 100,model_session_id=session.descriptor["session_id"])
|
|
195
|
+
return await session.wait(asyncio.to_thread(observe_operation,manager,operation,stop_event=stop))
|
|
196
|
+
except BaseException as error:
|
|
197
|
+
stop.set()
|
|
198
|
+
if operation is not None:
|
|
199
|
+
try:
|
|
200
|
+
await asyncio.to_thread(manager.management.stop,'run',operation['run_id'])
|
|
201
|
+
except Exception as cancellation:
|
|
202
|
+
error.add_note('Run cancellation not confirmed: '+type(cancellation).__name__)
|
|
203
|
+
raise
|
|
204
|
+
return asyncio.run(connected())
|
|
205
|
+
return observe_operation(manager, manager.submit_run(run, identifier=identifier, name=args.name, batch_size=args.batch_size or 100))
|
|
206
|
+
if args.id:
|
|
207
|
+
parser.error("--id requires a managed Hub dataset selection")
|
|
208
|
+
hub_endpoint = connection.hub
|
|
209
|
+
if not hub_endpoint:
|
|
210
|
+
parser.error("Task preparation requires a Hub address")
|
|
211
|
+
if not (args.samples or args.hub_selection or embedded_hub):
|
|
212
|
+
parser.error("--run requires an inline Hub dataset or --samples")
|
|
213
|
+
registry = SchemaRegistry.bundled()
|
|
214
|
+
from uenv.sdk.hub import HubClient
|
|
215
|
+
hub = HubClient(hub_endpoint)
|
|
216
|
+
samples = (
|
|
217
|
+
args.samples
|
|
218
|
+
if args.raw_package
|
|
219
|
+
else [
|
|
220
|
+
json.loads(line)
|
|
221
|
+
for line in args.samples.read_text(encoding="utf-8").splitlines()
|
|
222
|
+
if line.strip()
|
|
223
|
+
]
|
|
224
|
+
if args.samples
|
|
225
|
+
else None
|
|
226
|
+
)
|
|
227
|
+
with UEnvClient(
|
|
228
|
+
args.endpoint,
|
|
229
|
+
|
|
230
|
+
registry=registry,
|
|
231
|
+
hub=hub if not (args.hub_selection or embedded_hub) else None,
|
|
232
|
+
) as client:
|
|
233
|
+
if args.hub_selection or embedded_hub:
|
|
234
|
+
if args.hub_selection:
|
|
235
|
+
selection = load_yaml(args.hub_selection)
|
|
236
|
+
registry.validate("HubSampleQuery", selection)
|
|
237
|
+
print(
|
|
238
|
+
"warning: --hub-selection is deprecated; put dataset in --run",
|
|
239
|
+
file=sys.stderr,
|
|
240
|
+
)
|
|
241
|
+
prepared = client.prepare_hub(run, hub=hub, **selection)
|
|
242
|
+
else:
|
|
243
|
+
prepared = client.prepare_hub(run, hub=hub)
|
|
244
|
+
else:
|
|
245
|
+
if args.dataset:
|
|
246
|
+
print(
|
|
247
|
+
"warning: --dataset is deprecated; put dataset in --run",
|
|
248
|
+
file=sys.stderr,
|
|
249
|
+
)
|
|
250
|
+
prepared = (
|
|
251
|
+
client.prepare_raw(
|
|
252
|
+
run,
|
|
253
|
+
samples,
|
|
254
|
+
package_directory=args.raw_package,
|
|
255
|
+
**(
|
|
256
|
+
{"dataset": load_yaml(args.dataset)}
|
|
257
|
+
if args.dataset
|
|
258
|
+
else {}
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
if args.raw_package
|
|
262
|
+
else client.prepare(run, samples)
|
|
263
|
+
)
|
|
264
|
+
def print_receipt(receipt):
|
|
265
|
+
print(
|
|
266
|
+
json.dumps(
|
|
267
|
+
{"run_id": prepared["run_spec"]["run_id"], "receipt": receipt},
|
|
268
|
+
ensure_ascii=False,
|
|
269
|
+
),
|
|
270
|
+
file=sys.stderr if args.wait else sys.stdout,
|
|
271
|
+
flush=True,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if not args.direct:
|
|
275
|
+
from .management_gateway import ManagementGatewayClient
|
|
276
|
+
from .evaluation import observe_prepared
|
|
277
|
+
manager = ManagementGatewayClient(connection.management, token_file=connection.management_token_file, registry=client.registry)
|
|
278
|
+
operation = manager.submit_prepared(prepared, batch_size=args.batch_size or 100, name=args.name)
|
|
279
|
+
print(json.dumps(operation, ensure_ascii=False), file=sys.stderr if args.wait else sys.stdout, flush=True)
|
|
280
|
+
if args.wait:
|
|
281
|
+
summary = observe_prepared(manager, prepared, output=args.output)
|
|
282
|
+
print(json.dumps(summary, ensure_ascii=False), file=sys.stderr)
|
|
283
|
+
return summary["exit_code"]
|
|
284
|
+
return 0
|
|
285
|
+
if args.wait:
|
|
286
|
+
summary = client.evaluate_prepared(prepared, batch_size=args.batch_size or 100,
|
|
287
|
+
output=args.output, on_submitted=print_receipt)
|
|
288
|
+
print(json.dumps(summary, ensure_ascii=False), file=sys.stderr)
|
|
289
|
+
return summary["exit_code"]
|
|
290
|
+
print_receipt(client.submit_prepared(prepared))
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == "__main__":
|
|
294
|
+
raise SystemExit(main())
|