flowmesh-cli-stack 0.1.0__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.
- flowmesh_cli_stack/__init__.py +13 -0
- flowmesh_cli_stack/assets/.env.example +204 -0
- flowmesh_cli_stack/assets/compose.yml +201 -0
- flowmesh_cli_stack/assets/docker-bake.hcl +110 -0
- flowmesh_cli_stack/bundle.py +384 -0
- flowmesh_cli_stack/env_schema.py +646 -0
- flowmesh_cli_stack/stack.py +789 -0
- flowmesh_cli_stack/utils.py +137 -0
- flowmesh_cli_stack/worker.py +235 -0
- flowmesh_cli_stack-0.1.0.dist-info/METADATA +25 -0
- flowmesh_cli_stack-0.1.0.dist-info/RECORD +14 -0
- flowmesh_cli_stack-0.1.0.dist-info/WHEEL +5 -0
- flowmesh_cli_stack-0.1.0.dist-info/licenses/LICENSE +202 -0
- flowmesh_cli_stack-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
"""Stack env schema."""
|
|
2
|
+
|
|
3
|
+
from flowmesh.models.nodes import NodeRole
|
|
4
|
+
from flowmesh_stack.env_schema import (
|
|
5
|
+
EnvSchema,
|
|
6
|
+
EnvSection,
|
|
7
|
+
EnvVar,
|
|
8
|
+
EnvVarType,
|
|
9
|
+
require_all_or_none,
|
|
10
|
+
require_if_true,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
STACK_ENV_SCHEMA = EnvSchema(
|
|
14
|
+
name="stack",
|
|
15
|
+
header=[
|
|
16
|
+
"# FlowMesh Stack Configuration",
|
|
17
|
+
"# Copy to .env and adjust as needed",
|
|
18
|
+
],
|
|
19
|
+
sections=[
|
|
20
|
+
EnvSection(
|
|
21
|
+
title="Image Source",
|
|
22
|
+
vars=[
|
|
23
|
+
EnvVar("FLOWMESH_REGISTRY", "ghcr.io/mlsys-io", required=True),
|
|
24
|
+
EnvVar("FLOWMESH_VERSION", "dev", required=True),
|
|
25
|
+
EnvVar(
|
|
26
|
+
"FLOWMESH_CACHE_VERSION",
|
|
27
|
+
description=[
|
|
28
|
+
"Optional registry cache lineage for stack push.",
|
|
29
|
+
"Leave empty to use the default stable cache scope.",
|
|
30
|
+
],
|
|
31
|
+
),
|
|
32
|
+
EnvVar("FLOWMESH_BUILD_REF", "local"),
|
|
33
|
+
],
|
|
34
|
+
),
|
|
35
|
+
EnvSection(
|
|
36
|
+
title="Node Identity",
|
|
37
|
+
vars=[
|
|
38
|
+
EnvVar(
|
|
39
|
+
"FLOWMESH_STACK_SUFFIX",
|
|
40
|
+
description=[
|
|
41
|
+
"Optional suffix appended to stack-managed Docker object "
|
|
42
|
+
"names.",
|
|
43
|
+
"Use a distinct suffix per local stack on shared hosts.",
|
|
44
|
+
"This isolates container, network, and volume names,",
|
|
45
|
+
"but each stack still needs unique ports.",
|
|
46
|
+
],
|
|
47
|
+
),
|
|
48
|
+
EnvVar(
|
|
49
|
+
"NODE_ROLE",
|
|
50
|
+
NodeRole.ROOT.value,
|
|
51
|
+
var_type=EnvVarType.ENUM,
|
|
52
|
+
choices=NodeRole,
|
|
53
|
+
),
|
|
54
|
+
EnvVar("NODE_NAMESPACE", "flowmesh"),
|
|
55
|
+
EnvVar("NODE_CLUSTER", "dev"),
|
|
56
|
+
EnvVar("NODE_ALIAS", "node"),
|
|
57
|
+
EnvVar("NODE_TAGS", var_type=EnvVarType.CSV),
|
|
58
|
+
EnvVar("ENABLE_SUPERVISOR", "true", var_type=EnvVarType.BOOL),
|
|
59
|
+
EnvVar("SERVER_HOST", "localhost", required=True),
|
|
60
|
+
EnvVar(
|
|
61
|
+
"SERVER_HTTP_PORT",
|
|
62
|
+
"8000",
|
|
63
|
+
var_type=EnvVarType.INT,
|
|
64
|
+
required=True,
|
|
65
|
+
min_value=1,
|
|
66
|
+
),
|
|
67
|
+
EnvVar(
|
|
68
|
+
"SERVER_GRPC_PORT",
|
|
69
|
+
"50051",
|
|
70
|
+
var_type=EnvVarType.INT,
|
|
71
|
+
required=True,
|
|
72
|
+
min_value=1,
|
|
73
|
+
),
|
|
74
|
+
EnvVar(
|
|
75
|
+
"SERVER_LOG_LEVEL",
|
|
76
|
+
"INFO",
|
|
77
|
+
var_type=EnvVarType.LOG_LEVEL,
|
|
78
|
+
),
|
|
79
|
+
],
|
|
80
|
+
),
|
|
81
|
+
EnvSection(
|
|
82
|
+
title="Server gRPC TLS",
|
|
83
|
+
description=["Leave empty to disable"],
|
|
84
|
+
vars=[
|
|
85
|
+
EnvVar(
|
|
86
|
+
"SERVER_TLS_DIR",
|
|
87
|
+
"./secrets/tls/server",
|
|
88
|
+
var_type=EnvVarType.DIR_PATH,
|
|
89
|
+
use_default=True,
|
|
90
|
+
ensure_path="create",
|
|
91
|
+
),
|
|
92
|
+
EnvVar(
|
|
93
|
+
"SERVER_GRPC_TLS_CA_FILE",
|
|
94
|
+
"/etc/ssl/server/server-ca.pem",
|
|
95
|
+
var_type=EnvVarType.FILE_PATH,
|
|
96
|
+
),
|
|
97
|
+
EnvVar(
|
|
98
|
+
"SERVER_GRPC_TLS_CERT_FILE",
|
|
99
|
+
"/etc/ssl/server/server.pem",
|
|
100
|
+
var_type=EnvVarType.FILE_PATH,
|
|
101
|
+
),
|
|
102
|
+
EnvVar(
|
|
103
|
+
"SERVER_GRPC_TLS_KEY_FILE",
|
|
104
|
+
"/etc/ssl/server/server.key",
|
|
105
|
+
var_type=EnvVarType.FILE_PATH,
|
|
106
|
+
),
|
|
107
|
+
],
|
|
108
|
+
),
|
|
109
|
+
EnvSection(
|
|
110
|
+
title="Supervisor gRPC",
|
|
111
|
+
description=[
|
|
112
|
+
"Tuning for the supervisor's gRPC server and worker connections.",
|
|
113
|
+
"Leave SUPERVISOR_GRPC_EXTERNAL_PORT empty unless workers connect",
|
|
114
|
+
"through a port-forwarded / proxied address.",
|
|
115
|
+
],
|
|
116
|
+
vars=[
|
|
117
|
+
EnvVar(
|
|
118
|
+
"SUPERVISOR_GRPC_DISABLE_SERVER_TLS",
|
|
119
|
+
"false",
|
|
120
|
+
var_type=EnvVarType.BOOL,
|
|
121
|
+
),
|
|
122
|
+
EnvVar(
|
|
123
|
+
"SUPERVISOR_GRPC_EXTERNAL_PORT",
|
|
124
|
+
var_type=EnvVarType.INT,
|
|
125
|
+
min_value=1,
|
|
126
|
+
),
|
|
127
|
+
EnvVar(
|
|
128
|
+
"SUPERVISOR_GRPC_KEEPALIVE_PERMIT_WITHOUT_CALLS",
|
|
129
|
+
"true",
|
|
130
|
+
var_type=EnvVarType.BOOL,
|
|
131
|
+
),
|
|
132
|
+
EnvVar(
|
|
133
|
+
"SUPERVISOR_GRPC_MIN_RECV_PING_INTERVAL_MS",
|
|
134
|
+
"60000",
|
|
135
|
+
var_type=EnvVarType.INT,
|
|
136
|
+
min_value=0,
|
|
137
|
+
),
|
|
138
|
+
EnvVar(
|
|
139
|
+
"SUPERVISOR_GRPC_KEEPALIVE_TIME_MS",
|
|
140
|
+
"300000",
|
|
141
|
+
var_type=EnvVarType.INT,
|
|
142
|
+
min_value=0,
|
|
143
|
+
),
|
|
144
|
+
EnvVar(
|
|
145
|
+
"SUPERVISOR_GRPC_KEEPALIVE_TIMEOUT_MS",
|
|
146
|
+
"10000",
|
|
147
|
+
var_type=EnvVarType.INT,
|
|
148
|
+
min_value=0,
|
|
149
|
+
),
|
|
150
|
+
],
|
|
151
|
+
),
|
|
152
|
+
EnvSection(
|
|
153
|
+
title="Redis Connectivity",
|
|
154
|
+
vars=[
|
|
155
|
+
EnvVar(
|
|
156
|
+
"REDIS_CONTROL_URL",
|
|
157
|
+
"redis://localhost:6379/0",
|
|
158
|
+
var_type=EnvVarType.URL,
|
|
159
|
+
required=True,
|
|
160
|
+
url_schemes={"redis", "rediss"},
|
|
161
|
+
),
|
|
162
|
+
EnvVar(
|
|
163
|
+
"REDIS_TELEMETRY_URL",
|
|
164
|
+
"redis://localhost:6380/0",
|
|
165
|
+
var_type=EnvVarType.URL,
|
|
166
|
+
required=True,
|
|
167
|
+
url_schemes={"redis", "rediss"},
|
|
168
|
+
),
|
|
169
|
+
],
|
|
170
|
+
),
|
|
171
|
+
EnvSection(
|
|
172
|
+
title="Core Ports",
|
|
173
|
+
vars=[
|
|
174
|
+
EnvVar(
|
|
175
|
+
"REDIS_CONTROL_PORT",
|
|
176
|
+
"6379",
|
|
177
|
+
var_type=EnvVarType.INT,
|
|
178
|
+
required=True,
|
|
179
|
+
min_value=1,
|
|
180
|
+
),
|
|
181
|
+
EnvVar(
|
|
182
|
+
"REDIS_TELEMETRY_PORT",
|
|
183
|
+
"6380",
|
|
184
|
+
var_type=EnvVarType.INT,
|
|
185
|
+
required=True,
|
|
186
|
+
min_value=1,
|
|
187
|
+
),
|
|
188
|
+
],
|
|
189
|
+
),
|
|
190
|
+
EnvSection(
|
|
191
|
+
title="Log Streams (Redis)",
|
|
192
|
+
description=["Caps Redis Streams for per-task and per-workflow logs."],
|
|
193
|
+
vars=[
|
|
194
|
+
EnvVar(
|
|
195
|
+
"LOG_STREAM_MAXLEN_TASK",
|
|
196
|
+
"50000",
|
|
197
|
+
var_type=EnvVarType.INT,
|
|
198
|
+
min_value=1,
|
|
199
|
+
),
|
|
200
|
+
EnvVar(
|
|
201
|
+
"LOG_STREAM_MAXLEN_WORKFLOW",
|
|
202
|
+
"200000",
|
|
203
|
+
var_type=EnvVarType.INT,
|
|
204
|
+
min_value=1,
|
|
205
|
+
),
|
|
206
|
+
EnvVar(
|
|
207
|
+
"LOG_STREAM_TTL_SEC",
|
|
208
|
+
"3600",
|
|
209
|
+
description="Expire log stream keys after close (0 disables).",
|
|
210
|
+
var_type=EnvVarType.INT,
|
|
211
|
+
min_value=0,
|
|
212
|
+
),
|
|
213
|
+
EnvVar(
|
|
214
|
+
"TASK_LOG_ARCHIVE_FLUSH_INTERVAL_SEC",
|
|
215
|
+
"5",
|
|
216
|
+
description="Flush archived task logs at most every N seconds.",
|
|
217
|
+
var_type=EnvVarType.FLOAT,
|
|
218
|
+
min_value=0.1,
|
|
219
|
+
),
|
|
220
|
+
EnvVar(
|
|
221
|
+
"TASK_LOG_ARCHIVE_FLUSH_MAX_ENTRIES",
|
|
222
|
+
"100",
|
|
223
|
+
description="Flush archived task logs after buffering N entries.",
|
|
224
|
+
var_type=EnvVarType.INT,
|
|
225
|
+
min_value=1,
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
),
|
|
229
|
+
EnvSection(
|
|
230
|
+
title="Redis Access",
|
|
231
|
+
vars=[
|
|
232
|
+
EnvVar("REDIS_ACL_ENABLED", "1", var_type=EnvVarType.BOOL),
|
|
233
|
+
EnvVar("REDIS_USERNAME", "admin"),
|
|
234
|
+
EnvVar("REDIS_PASSWORD", "very-strong-password"),
|
|
235
|
+
],
|
|
236
|
+
),
|
|
237
|
+
EnvSection(
|
|
238
|
+
title="Redis TLS",
|
|
239
|
+
description=["Leave empty to disable"],
|
|
240
|
+
vars=[
|
|
241
|
+
EnvVar(
|
|
242
|
+
"REDIS_TLS_DIR",
|
|
243
|
+
"./secrets/tls/redis",
|
|
244
|
+
var_type=EnvVarType.DIR_PATH,
|
|
245
|
+
use_default=True,
|
|
246
|
+
ensure_path="create",
|
|
247
|
+
),
|
|
248
|
+
EnvVar(
|
|
249
|
+
"REDIS_TLS_CA_FILE",
|
|
250
|
+
"/etc/ssl/redis/redis-ca.pem",
|
|
251
|
+
var_type=EnvVarType.FILE_PATH,
|
|
252
|
+
),
|
|
253
|
+
EnvVar(
|
|
254
|
+
"REDIS_TLS_CERT_FILE",
|
|
255
|
+
"/etc/ssl/redis/redis-server.pem",
|
|
256
|
+
var_type=EnvVarType.FILE_PATH,
|
|
257
|
+
),
|
|
258
|
+
EnvVar(
|
|
259
|
+
"REDIS_TLS_KEY_FILE",
|
|
260
|
+
"/etc/ssl/redis/redis-server.key",
|
|
261
|
+
var_type=EnvVarType.FILE_PATH,
|
|
262
|
+
),
|
|
263
|
+
],
|
|
264
|
+
),
|
|
265
|
+
EnvSection(
|
|
266
|
+
title="SSH Task Support",
|
|
267
|
+
vars=[
|
|
268
|
+
EnvVar("ENABLE_SERVER_SSH_PROXY", "true", var_type=EnvVarType.BOOL),
|
|
269
|
+
EnvVar("ENABLE_SERVER_SSH_FORWARD", "true", var_type=EnvVarType.BOOL),
|
|
270
|
+
EnvVar(
|
|
271
|
+
"ENABLE_SERVER_SSH_CONNECTION_AUDIT",
|
|
272
|
+
"true",
|
|
273
|
+
var_type=EnvVarType.BOOL,
|
|
274
|
+
),
|
|
275
|
+
EnvVar("SERVER_SSH_FORWARD_BIND_HOST", "0.0.0.0", required=True),
|
|
276
|
+
EnvVar("SERVER_SSH_FORWARD_PUBLIC_HOST", "localhost", required=True),
|
|
277
|
+
EnvVar(
|
|
278
|
+
"SERVER_SSH_FORWARD_PORT_START",
|
|
279
|
+
"32000",
|
|
280
|
+
var_type=EnvVarType.INT,
|
|
281
|
+
required=True,
|
|
282
|
+
min_value=1,
|
|
283
|
+
),
|
|
284
|
+
EnvVar(
|
|
285
|
+
"SERVER_SSH_FORWARD_PORT_END",
|
|
286
|
+
"32100",
|
|
287
|
+
var_type=EnvVarType.INT,
|
|
288
|
+
required=True,
|
|
289
|
+
min_value=1,
|
|
290
|
+
),
|
|
291
|
+
],
|
|
292
|
+
),
|
|
293
|
+
EnvSection(
|
|
294
|
+
title="SSH Worker Defaults",
|
|
295
|
+
vars=[
|
|
296
|
+
EnvVar("ENABLE_SSH_BY_DEFAULT", "true", var_type=EnvVarType.BOOL),
|
|
297
|
+
EnvVar("SSH_DEFAULT_IMAGE"),
|
|
298
|
+
EnvVar("SSH_DEFAULT_USER"),
|
|
299
|
+
EnvVar("SSH_DEFAULT_TTL_SEC", var_type=EnvVarType.FLOAT, min_value=0),
|
|
300
|
+
EnvVar("SSH_DEFAULT_IDLE_SEC", var_type=EnvVarType.FLOAT, min_value=0),
|
|
301
|
+
EnvVar("SSH_MAX_TTL_SEC", var_type=EnvVarType.FLOAT, min_value=0),
|
|
302
|
+
EnvVar("SSH_POLL_INTERVAL_SEC", var_type=EnvVarType.FLOAT, min_value=0),
|
|
303
|
+
EnvVar("SSH_STOP_TIMEOUT_SEC", var_type=EnvVarType.FLOAT, min_value=0),
|
|
304
|
+
],
|
|
305
|
+
),
|
|
306
|
+
EnvSection(
|
|
307
|
+
title="General Settings",
|
|
308
|
+
vars=[
|
|
309
|
+
EnvVar("TZ", "Asia/Singapore", required=True),
|
|
310
|
+
EnvVar(
|
|
311
|
+
"LOG_LEVEL", "INFO", var_type=EnvVarType.LOG_LEVEL, required=True
|
|
312
|
+
),
|
|
313
|
+
],
|
|
314
|
+
),
|
|
315
|
+
EnvSection(
|
|
316
|
+
title="Orchestrator Settings",
|
|
317
|
+
vars=[
|
|
318
|
+
EnvVar(
|
|
319
|
+
"ORCHESTRATOR_DISPATCH_MODE",
|
|
320
|
+
"adaptive",
|
|
321
|
+
var_type=EnvVarType.ENUM,
|
|
322
|
+
choices={"adaptive"},
|
|
323
|
+
),
|
|
324
|
+
EnvVar(
|
|
325
|
+
"ORCHESTRATOR_WORKER_SELECTION",
|
|
326
|
+
"best_fit",
|
|
327
|
+
var_type=EnvVarType.ENUM,
|
|
328
|
+
choices={"best_fit", "first_fit", "min_satisfying"},
|
|
329
|
+
),
|
|
330
|
+
EnvVar(
|
|
331
|
+
"SCHEDULER_SELECTION_JITTER",
|
|
332
|
+
"0.001",
|
|
333
|
+
var_type=EnvVarType.FLOAT,
|
|
334
|
+
min_value=0.0,
|
|
335
|
+
),
|
|
336
|
+
EnvVar(
|
|
337
|
+
"SCHEDULER_LAMBDA_INFERENCE",
|
|
338
|
+
"0.4",
|
|
339
|
+
var_type=EnvVarType.FLOAT,
|
|
340
|
+
min_value=0.0,
|
|
341
|
+
),
|
|
342
|
+
EnvVar(
|
|
343
|
+
"SCHEDULER_LAMBDA_TRAINING",
|
|
344
|
+
"0.8",
|
|
345
|
+
var_type=EnvVarType.FLOAT,
|
|
346
|
+
min_value=0.0,
|
|
347
|
+
),
|
|
348
|
+
EnvVar(
|
|
349
|
+
"SCHEDULER_LAMBDA_OTHER",
|
|
350
|
+
"0.5",
|
|
351
|
+
var_type=EnvVarType.FLOAT,
|
|
352
|
+
min_value=0.0,
|
|
353
|
+
),
|
|
354
|
+
EnvVar("ENABLE_TASK_MERGE", "true", var_type=EnvVarType.BOOL),
|
|
355
|
+
EnvVar(
|
|
356
|
+
"TASK_MERGE_MAX_BATCH_SIZE",
|
|
357
|
+
"4",
|
|
358
|
+
var_type=EnvVarType.INT,
|
|
359
|
+
min_value=1,
|
|
360
|
+
),
|
|
361
|
+
EnvVar("ENABLE_CONTEXT_REUSE", "true", var_type=EnvVarType.BOOL),
|
|
362
|
+
EnvVar(
|
|
363
|
+
"WORKER_CACHE_TTL_SEC",
|
|
364
|
+
"3600",
|
|
365
|
+
var_type=EnvVarType.INT,
|
|
366
|
+
min_value=0,
|
|
367
|
+
),
|
|
368
|
+
EnvVar(
|
|
369
|
+
"ENABLE_STAGE_WEIGHT_STICKINESS",
|
|
370
|
+
"false",
|
|
371
|
+
var_type=EnvVarType.BOOL,
|
|
372
|
+
),
|
|
373
|
+
EnvVar("ENABLE_WORKER_WATCHDOG", "true", var_type=EnvVarType.BOOL),
|
|
374
|
+
EnvVar(
|
|
375
|
+
"WORKER_DEATH_CHECK_INTERVAL",
|
|
376
|
+
"30",
|
|
377
|
+
var_type=EnvVarType.INT,
|
|
378
|
+
min_value=5,
|
|
379
|
+
),
|
|
380
|
+
EnvVar(
|
|
381
|
+
"WORKER_DEATH_GRACE_SEC",
|
|
382
|
+
"60",
|
|
383
|
+
var_type=EnvVarType.INT,
|
|
384
|
+
min_value=0,
|
|
385
|
+
),
|
|
386
|
+
],
|
|
387
|
+
),
|
|
388
|
+
EnvSection(
|
|
389
|
+
title="Server Heartbeat",
|
|
390
|
+
vars=[
|
|
391
|
+
EnvVar(
|
|
392
|
+
"SERVER_HEARTBEAT_INTERVAL",
|
|
393
|
+
"30",
|
|
394
|
+
var_type=EnvVarType.INT,
|
|
395
|
+
min_value=1,
|
|
396
|
+
)
|
|
397
|
+
],
|
|
398
|
+
),
|
|
399
|
+
EnvSection(
|
|
400
|
+
title="Vast.ai Configuration",
|
|
401
|
+
vars=[
|
|
402
|
+
EnvVar("VAST_SEARCH_LIMIT", var_type=EnvVarType.INT, min_value=0),
|
|
403
|
+
EnvVar("VAST_MAX_RETRIES", var_type=EnvVarType.INT, min_value=0),
|
|
404
|
+
],
|
|
405
|
+
),
|
|
406
|
+
EnvSection(
|
|
407
|
+
title="Worker Parameters",
|
|
408
|
+
vars=[
|
|
409
|
+
EnvVar("WORKER_LOG_LEVEL", "INFO", var_type=EnvVarType.LOG_LEVEL),
|
|
410
|
+
EnvVar(
|
|
411
|
+
"HEARTBEAT_INTERVAL_SEC", "30", var_type=EnvVarType.INT, min_value=1
|
|
412
|
+
),
|
|
413
|
+
EnvVar(
|
|
414
|
+
"WORKER_COST_PER_HOUR",
|
|
415
|
+
"1.0",
|
|
416
|
+
var_type=EnvVarType.FLOAT,
|
|
417
|
+
min_value=0.0,
|
|
418
|
+
),
|
|
419
|
+
EnvVar(
|
|
420
|
+
"SERVER_RESULTS_DIR",
|
|
421
|
+
var_type=EnvVarType.DIR_PATH,
|
|
422
|
+
description=[
|
|
423
|
+
"Directory/Docker volume for the server to look up task "
|
|
424
|
+
"results after worker completion.",
|
|
425
|
+
"Set to the same value as WORKER_RESULTS_DIR so the server "
|
|
426
|
+
"can access worker results.",
|
|
427
|
+
"For workflows with a local output destination "
|
|
428
|
+
'(`spec.output.destination.type="local"`),',
|
|
429
|
+
"`SERVER_RESULTS_DIR` and `WORKER_RESULTS_DIR` must point to "
|
|
430
|
+
"the same shared directory",
|
|
431
|
+
"or volume; otherwise, the server cannot read the worker's "
|
|
432
|
+
"outputs and downstream tasks",
|
|
433
|
+
"will stall in the dispatching loop.",
|
|
434
|
+
"Defaults to the stack-scoped results volume when empty.",
|
|
435
|
+
],
|
|
436
|
+
),
|
|
437
|
+
EnvVar(
|
|
438
|
+
"WORKER_RESULTS_DIR",
|
|
439
|
+
var_type=EnvVarType.DIR_PATH,
|
|
440
|
+
description=[
|
|
441
|
+
"Defaults to the stack-scoped results volume when empty."
|
|
442
|
+
],
|
|
443
|
+
),
|
|
444
|
+
EnvVar("HF_CACHE_DIR", var_type=EnvVarType.DIR_PATH),
|
|
445
|
+
EnvVar(
|
|
446
|
+
"WORKER_NETWORK_BANDWIDTH_BYTES_PER_SEC",
|
|
447
|
+
var_type=EnvVarType.INT,
|
|
448
|
+
min_value=1,
|
|
449
|
+
),
|
|
450
|
+
EnvVar("WORKER_TAGS", var_type=EnvVarType.CSV),
|
|
451
|
+
EnvVar("WORKER_HB_DIR", var_type=EnvVarType.DIR_PATH),
|
|
452
|
+
EnvVar(
|
|
453
|
+
"FLOWMESH_BASE_URL",
|
|
454
|
+
"http://localhost:8000",
|
|
455
|
+
var_type=EnvVarType.URL,
|
|
456
|
+
required=True,
|
|
457
|
+
url_schemes={"http", "https"},
|
|
458
|
+
),
|
|
459
|
+
EnvVar(
|
|
460
|
+
"FLOWMESH_API_KEY",
|
|
461
|
+
description="Supplier API key for worker authentication with "
|
|
462
|
+
"the server",
|
|
463
|
+
),
|
|
464
|
+
EnvVar(
|
|
465
|
+
"NEBULA_API_BASE_URL",
|
|
466
|
+
var_type=EnvVarType.URL,
|
|
467
|
+
url_schemes={"http", "https"},
|
|
468
|
+
),
|
|
469
|
+
EnvVar(
|
|
470
|
+
"SERVER_CUDA_PROBE_IMAGE",
|
|
471
|
+
"nvidia/cuda:12.9.1-base-ubuntu24.04",
|
|
472
|
+
description="Server-side CUDA image used to probe local GPUs.",
|
|
473
|
+
),
|
|
474
|
+
EnvVar(
|
|
475
|
+
"DOCKER_GPU_RUNTIME",
|
|
476
|
+
"nvidia",
|
|
477
|
+
description="Optional Docker runtime name for GPU containers.",
|
|
478
|
+
),
|
|
479
|
+
EnvVar(
|
|
480
|
+
"CUDA_VISIBLE_DEVICES", "all", var_type=EnvVarType.CSV_INTS_OR_ALL
|
|
481
|
+
),
|
|
482
|
+
EnvVar("WORKER_UPLOAD_RESULTS", "false", var_type=EnvVarType.BOOL),
|
|
483
|
+
EnvVar(
|
|
484
|
+
"MODEL_CLEANUP_AFTER_UPLOAD",
|
|
485
|
+
"0",
|
|
486
|
+
var_type=EnvVarType.INT,
|
|
487
|
+
min_value=0,
|
|
488
|
+
),
|
|
489
|
+
],
|
|
490
|
+
),
|
|
491
|
+
EnvSection(
|
|
492
|
+
title="Model Pre-downloading",
|
|
493
|
+
description=[
|
|
494
|
+
"Comma-separated list of models to pre-download during worker startup",
|
|
495
|
+
"Leave empty to disable model pre-downloading",
|
|
496
|
+
"Example: meta-llama/Llama-3.2-1B-Instruct,"
|
|
497
|
+
"meta-llama/Llama-3.2-3B-Instruct",
|
|
498
|
+
],
|
|
499
|
+
vars=[EnvVar("PREDOWNLOAD_MODEL_LIST", var_type=EnvVarType.CSV)],
|
|
500
|
+
),
|
|
501
|
+
EnvSection(
|
|
502
|
+
title="API Keys injected into workers (optional)",
|
|
503
|
+
vars=[
|
|
504
|
+
EnvVar("OPENAI_API_KEY"),
|
|
505
|
+
EnvVar("GOOGLE_API_KEY"),
|
|
506
|
+
EnvVar("VAST_API_KEY"),
|
|
507
|
+
EnvVar("HF_TOKEN"),
|
|
508
|
+
EnvVar("NEBULA_API_TOKEN"),
|
|
509
|
+
],
|
|
510
|
+
),
|
|
511
|
+
EnvSection(
|
|
512
|
+
title="External Plugins",
|
|
513
|
+
description=[
|
|
514
|
+
"Plugins are Python packages dropped under FLOWMESH_PLUGIN_DIR ",
|
|
515
|
+
"(host-mounted to /app/plugins on the server) and selected by ",
|
|
516
|
+
"FLOWMESH_PLUGINS as a comma-separated list of top-level module ",
|
|
517
|
+
"names. Each named module must expose `install()` returning a ",
|
|
518
|
+
"`HookBindings`. Leave both empty unless you ship a plugin.",
|
|
519
|
+
],
|
|
520
|
+
vars=[
|
|
521
|
+
EnvVar(
|
|
522
|
+
"FLOWMESH_PLUGIN_DIR",
|
|
523
|
+
"./plugins",
|
|
524
|
+
var_type=EnvVarType.DIR_PATH,
|
|
525
|
+
use_default=True,
|
|
526
|
+
ensure_path="create",
|
|
527
|
+
),
|
|
528
|
+
EnvVar("FLOWMESH_PLUGINS", ""),
|
|
529
|
+
],
|
|
530
|
+
),
|
|
531
|
+
EnvSection(
|
|
532
|
+
title="Agent Executor (youtu-agent / utu)",
|
|
533
|
+
description=[
|
|
534
|
+
"All four UTU_LLM_* are required for the agent executor to run."
|
|
535
|
+
],
|
|
536
|
+
vars=[
|
|
537
|
+
EnvVar(
|
|
538
|
+
"UTU_LLM_TYPE",
|
|
539
|
+
description='utu LLM provider kind, e.g. "chat.completions"',
|
|
540
|
+
),
|
|
541
|
+
EnvVar(
|
|
542
|
+
"UTU_LLM_MODEL",
|
|
543
|
+
description="utu model identifier, e.g. gpt-4o-mini",
|
|
544
|
+
),
|
|
545
|
+
EnvVar(
|
|
546
|
+
"UTU_LLM_BASE_URL",
|
|
547
|
+
description="utu LLM base URL",
|
|
548
|
+
var_type=EnvVarType.URL,
|
|
549
|
+
url_schemes={"http", "https"},
|
|
550
|
+
),
|
|
551
|
+
EnvVar(
|
|
552
|
+
"UTU_LLM_API_KEY",
|
|
553
|
+
description="utu LLM API key",
|
|
554
|
+
),
|
|
555
|
+
EnvVar(
|
|
556
|
+
"SERPER_API_KEY",
|
|
557
|
+
description="Serper API key (optional, for agent search tools)",
|
|
558
|
+
),
|
|
559
|
+
EnvVar(
|
|
560
|
+
"JINA_API_KEY",
|
|
561
|
+
description="Jina API key (optional, for agent search tools)",
|
|
562
|
+
),
|
|
563
|
+
EnvVar(
|
|
564
|
+
"DB_URL",
|
|
565
|
+
description="Database URL for agent tracing (optional)",
|
|
566
|
+
),
|
|
567
|
+
],
|
|
568
|
+
),
|
|
569
|
+
EnvSection(
|
|
570
|
+
title="n8n Integration",
|
|
571
|
+
vars=[
|
|
572
|
+
EnvVar(
|
|
573
|
+
"N8N_CREDENTIAL_AES_PASSWORD",
|
|
574
|
+
description="AES-GCM key to decrypt encrypted n8n credentials.",
|
|
575
|
+
warn_if_empty=True,
|
|
576
|
+
)
|
|
577
|
+
],
|
|
578
|
+
),
|
|
579
|
+
EnvSection(
|
|
580
|
+
title="Worker launch config (optional)",
|
|
581
|
+
vars=[
|
|
582
|
+
EnvVar(
|
|
583
|
+
"SERVER_WORKER_CONFIG",
|
|
584
|
+
"./configs/worker_config.yaml",
|
|
585
|
+
var_type=EnvVarType.FILE_PATH,
|
|
586
|
+
use_default=True,
|
|
587
|
+
ensure_path="create",
|
|
588
|
+
)
|
|
589
|
+
],
|
|
590
|
+
),
|
|
591
|
+
EnvSection(
|
|
592
|
+
title="Logging",
|
|
593
|
+
vars=[
|
|
594
|
+
EnvVar(
|
|
595
|
+
"LOG_MAX_BYTES",
|
|
596
|
+
"5242880",
|
|
597
|
+
var_type=EnvVarType.INT,
|
|
598
|
+
min_value=1,
|
|
599
|
+
),
|
|
600
|
+
EnvVar(
|
|
601
|
+
"LOG_BACKUP_COUNT",
|
|
602
|
+
"5",
|
|
603
|
+
var_type=EnvVarType.INT,
|
|
604
|
+
min_value=0,
|
|
605
|
+
),
|
|
606
|
+
EnvVar("SERVER_APP_RELOAD", "0", var_type=EnvVarType.BOOL),
|
|
607
|
+
EnvVar("SERVER_APP_LOG_LEVEL", "info", var_type=EnvVarType.LOG_LEVEL),
|
|
608
|
+
],
|
|
609
|
+
),
|
|
610
|
+
],
|
|
611
|
+
validators=[
|
|
612
|
+
lambda env, errors, warnings: require_if_true(
|
|
613
|
+
env, "REDIS_ACL_ENABLED", ["REDIS_USERNAME", "REDIS_PASSWORD"], errors
|
|
614
|
+
),
|
|
615
|
+
lambda env, errors, warnings: require_all_or_none(
|
|
616
|
+
env,
|
|
617
|
+
[
|
|
618
|
+
"SERVER_GRPC_TLS_CA_FILE",
|
|
619
|
+
"SERVER_GRPC_TLS_CERT_FILE",
|
|
620
|
+
"SERVER_GRPC_TLS_KEY_FILE",
|
|
621
|
+
],
|
|
622
|
+
errors,
|
|
623
|
+
),
|
|
624
|
+
],
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
# Schema-default overrides applied when rendering a worker-role .env.
|
|
629
|
+
# Unused vars are blanked out to avoid confusion and misconfiguration.
|
|
630
|
+
WORKER_ROLE_OVERRIDES = {
|
|
631
|
+
"NODE_ROLE": NodeRole.WORKER.value,
|
|
632
|
+
"REDIS_TLS_CERT_FILE": "",
|
|
633
|
+
"REDIS_TLS_KEY_FILE": "",
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def role_overrides(role: NodeRole) -> dict[str, str]:
|
|
638
|
+
"""Return the schema-default overrides for a given role's rendered .env."""
|
|
639
|
+
return WORKER_ROLE_OVERRIDES.copy() if role == NodeRole.WORKER else {}
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def deploy_overrides(deploy: bool, version: str | None = None) -> dict[str, str]:
|
|
643
|
+
"""Return the schema-default overrides for a deploy-shaped rendered .env."""
|
|
644
|
+
if not (deploy and version):
|
|
645
|
+
return {}
|
|
646
|
+
return {"FLOWMESH_VERSION": version}
|