@platforma-sdk/workflow-tengo 4.6.4 → 4.7.0

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.
Files changed (37) hide show
  1. package/dist/tengo/lib/exec.constants.lib.tengo +6 -2
  2. package/dist/tengo/lib/exec.lib.tengo +9 -6
  3. package/dist/tengo/lib/exec.limits.lib.tengo +38 -13
  4. package/dist/tengo/lib/exec.runcmd.lib.tengo +35 -23
  5. package/dist/tengo/lib/feats.lib.tengo +15 -0
  6. package/dist/tengo/lib/smart.lib.tengo +12 -0
  7. package/dist/tengo/lib/tpl.base.lib.tengo +3 -0
  8. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  9. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  10. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  11. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  12. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  13. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  14. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  15. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  16. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  17. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  18. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  19. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  20. package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
  21. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  22. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  23. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  24. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  25. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  26. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  27. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  28. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  29. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  30. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  31. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  32. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  33. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  34. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  35. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  36. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  37. package/package.json +1 -1
@@ -1,21 +1,25 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+
1
3
  ARG_TYPE_STRING := "string"
2
4
  ARG_TYPE_EXPRESSION := "expressionRef"
3
5
  ARG_TYPE_VAR := "variableRef" // TODO: deprecated, remove after 15 Mar 2025
4
6
  ARG_TYPES := [ARG_TYPE_STRING, ARG_TYPE_EXPRESSION, ARG_TYPE_VAR]
5
7
 
8
+ BATCH_QUEUE := "batch" // special queue in storage controller with no limits
6
9
  HEAVY_QUEUE := "heavy"
7
10
  MEDIUM_QUEUE := "medium"
8
11
  LIGHT_QUEUE := "light"
9
12
  UI_TASKS_QUEUE := "ui-tasks"
10
13
 
11
- export {
14
+ export ll.toStrict({
12
15
  ARG_TYPE_STRING: ARG_TYPE_STRING,
13
16
  ARG_TYPE_EXPRESSION: ARG_TYPE_EXPRESSION,
14
17
  ARG_TYPE_VAR: ARG_TYPE_VAR,
15
18
  ARG_TYPES: ARG_TYPES,
16
19
 
20
+ BATCH_QUEUE: BATCH_QUEUE,
17
21
  UI_TASKS_QUEUE: UI_TASKS_QUEUE,
18
22
  LIGHT_QUEUE: LIGHT_QUEUE,
19
23
  MEDIUM_QUEUE: MEDIUM_QUEUE,
20
24
  HEAVY_QUEUE: HEAVY_QUEUE
21
- }
25
+ })
@@ -759,12 +759,15 @@ builder := func() {
759
759
  }
760
760
 
761
761
  serviceExecInputs := {}
762
- if feats.workdirLimits || feats.computeLimits {
763
- serviceExecInputs.quota = limits.quotaBuilder().
764
- queue(queue).
765
- cpu(cpuRequest). // 'undefined' value is OK and makes runner to apply default queue limits
766
- ram(ramRequest). // 'undefined' value is OK and makes runner to apply default queue limits
767
- build()
762
+ if feats.workdirLimits || feats.computeLimits || limits.useBatch(queue) {
763
+ if (is_int(ramRequest)) {
764
+ ramRequest = string(ramRequest) + "b"
765
+ }
766
+ serviceExecInputs.quota = {
767
+ queue: queue,
768
+ cpu: cpuRequest,
769
+ ram: ramRequest
770
+ }
768
771
  }
769
772
 
770
773
 
@@ -9,8 +9,11 @@ ll := import("@platforma-sdk/workflow-tengo:ll")
9
9
 
10
10
  oop := import("@platforma-sdk/workflow-tengo:oop")
11
11
  validation := import("@platforma-sdk/workflow-tengo:validation")
12
+ slices := import("@platforma-sdk/workflow-tengo:slices")
12
13
  smart := import("@platforma-sdk/workflow-tengo:smart")
13
14
  json := import("json")
15
+ constants := import("@platforma-sdk/workflow-tengo:exec.constants")
16
+ feats := import("@platforma-sdk/workflow-tengo:feats")
14
17
 
15
18
  _RTYPE_QUOTA := { Name: "Quota", Version: "1" }
16
19
 
@@ -19,6 +22,16 @@ _RTYPE_QUOTA := { Name: "Quota", Version: "1" }
19
22
 
20
23
 
21
24
 
25
+
26
+ useBatch := func(queueName) {
27
+ return feats.hasBatch && !slices.hasElement([ constants.UI_TASKS_QUEUE ], queueName)
28
+ }
29
+
30
+
31
+
32
+
33
+
34
+
22
35
  quotaBuilder := func() {
23
36
  self := undefined
24
37
 
@@ -132,9 +145,11 @@ storageSpaceRequestBuilder := func() {
132
145
  requestBuilder := smart.ephemeralBuilder(_RTYPE_STORAGE_SPACE_REQUEST)
133
146
  requestBuilder.getField(_SSR_FIELD_REQUEST).set(quota)
134
147
 
148
+ requestRef := requestBuilder.lockAndBuild()
149
+
135
150
  return ll.toStrict(
136
- oop.inherit(requestBuilder, {
137
- allocation: requestBuilder.getField(_SSR_FIELD_ALLOCATION)
151
+ oop.inherit(requestRef, {
152
+ allocation: requestRef.getField(_SSR_FIELD_ALLOCATION)
138
153
  })
139
154
  )
140
155
  }
@@ -143,8 +158,9 @@ storageSpaceRequestBuilder := func() {
143
158
  return self
144
159
  }
145
160
 
146
- _RTYPE_COMPUTE_REQUEST := func(executor) { return { Name: "ComputeRequest/" + executor, Version: "1" } }
147
- _CR_DEFAULT_EXECUTOR_ID := "executor"
161
+ _RTYPE_COMPUTE_REQUEST_EXECUTOR_V1 := { Name: "ComputeRequest/executor", Version: "1" }
162
+ _RTYPE_COMPUTE_REQUEST_BATCH_V1 := { Name: "ComputeRequest/batch", Version: "1" }
163
+
148
164
  _CR_FIELD_REQUEST := "request" // input
149
165
  _CR_FIELD_ALLOCATION := "allocation" // output
150
166
 
@@ -152,7 +168,7 @@ computeRequestBuilder := func() {
152
168
  self := undefined
153
169
 
154
170
  quota := undefined
155
- executor := _CR_DEFAULT_EXECUTOR_ID
171
+ queue_name := undefined
156
172
 
157
173
  self = ll.toStrict({
158
174
 
@@ -173,10 +189,9 @@ computeRequestBuilder := func() {
173
189
 
174
190
 
175
191
 
176
-
177
- executor: func(execID) {
178
- validation.assertType(execID, "string", "exec.limits.executor: executor ID should be a string")
179
- executor = execID
192
+ queue: func(name) {
193
+ validation.assertType(name, "string", "exec.limits.queue: queue name should be a string")
194
+ queue_name = name
180
195
 
181
196
  return self
182
197
  },
@@ -188,14 +203,23 @@ computeRequestBuilder := func() {
188
203
 
189
204
  build: func() {
190
205
  ll.assert(smart.isReference(quota), "exec.limits.build: quota of ComputeRequest must be a valid reference")
206
+ ll.assert(queue_name != "", "exec.limits.build: queue name is required for computeRequestBuilder")
207
+
208
+ requestBuilder := undefined
209
+
210
+ if useBatch(queue_name) {
211
+ requestBuilder = smart.ephemeralBuilder(_RTYPE_COMPUTE_REQUEST_BATCH_V1)
212
+ } else {
213
+ requestBuilder = smart.ephemeralBuilder(_RTYPE_COMPUTE_REQUEST_EXECUTOR_V1)
214
+ }
191
215
 
192
- rType := _RTYPE_COMPUTE_REQUEST(executor)
193
- requestBuilder := smart.ephemeralBuilder(rType)
194
216
  requestBuilder.getField(_CR_FIELD_REQUEST).set(quota)
195
217
 
218
+ requestRef := requestBuilder.lockAndBuild()
219
+
196
220
  return ll.toStrict(
197
- oop.inherit(requestBuilder, {
198
- allocation: requestBuilder.getField(_CR_FIELD_ALLOCATION)
221
+ oop.inherit(requestRef, {
222
+ allocation: requestRef.getField(_CR_FIELD_ALLOCATION)
199
223
  })
200
224
  )
201
225
  }
@@ -205,6 +229,7 @@ computeRequestBuilder := func() {
205
229
  }
206
230
 
207
231
  export ll.toStrict({
232
+ useBatch: useBatch,
208
233
  quotaBuilder: quotaBuilder,
209
234
  storageSpaceRequestBuilder: storageSpaceRequestBuilder,
210
235
  computeRequestBuilder: computeRequestBuilder
@@ -15,7 +15,8 @@ slices := import("@platforma-sdk/workflow-tengo:slices")
15
15
  limits := import("@platforma-sdk/workflow-tengo:exec.limits")
16
16
 
17
17
  _RTYPE_RUN_COMMAND_V1 := { Name: "RunCommand/executor", Version: "1" }
18
- _RTYPE_RUN_COMMAND_V2 := { Name: "RunCommand/executor", Version: "2" }
18
+ _RTYPE_RUN_COMMAND_EXEC_V2 := { Name: "RunCommand/executor", Version: "2" }
19
+ _RTYPE_RUN_COMMAND_BATCH_V2 := { Name: "RunCommand/batch", Version: "2" }
19
20
  _RTYPE_RUN_COMMAND_CMD := { Name: "RunCommandCmd", Version: "1" }
20
21
  _RTYPE_RUN_COMMAND_ARGS := { Name: "RunCommandArgs", Version: "1" }
21
22
  _RTYPE_RUN_COMMAND_OPTIONS := { Name: "run-command/options", Version: "1" }
@@ -77,16 +78,6 @@ _createExpressionArg := func(argTpl) {
77
78
 
78
79
 
79
80
  builder := func(workdir) {
80
- res := undefined
81
-
82
- if feats.computeLimits {
83
- res = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V2)
84
- } else {
85
- res = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V1)
86
- }
87
-
88
- res.getField(_FIELD_WORKDIR_IN).set(workdir)
89
-
90
81
  self := undefined
91
82
 
92
83
  cmd := undefined
@@ -105,7 +96,7 @@ builder := func(workdir) {
105
96
  stderr := "stderr.txt"
106
97
  nErrorLines := 200
107
98
 
108
- self = ll.toStrict(oop.inherit(res, {
99
+ self = ll.toStrict({
109
100
 
110
101
 
111
102
 
@@ -387,6 +378,25 @@ builder := func(workdir) {
387
378
 
388
379
 
389
380
  build: func() {
381
+ runRes := undefined
382
+
383
+ useBatch := limits.useBatch(queue)
384
+
385
+ if useBatch {
386
+
387
+
388
+
389
+ runRes = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_BATCH_V2)
390
+
391
+ } else if feats.computeLimits {
392
+ runRes = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_EXEC_V2)
393
+
394
+ } else {
395
+ runRes = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V1)
396
+ }
397
+
398
+ runRes.getField(_FIELD_WORKDIR_IN).set(workdir)
399
+
390
400
 
391
401
 
392
402
  ll.assert(!hasPatternArgs || len(substRules) != 0,
@@ -396,21 +406,23 @@ builder := func(workdir) {
396
406
  "runcmd.builder().build: expression arguments must be enabled only if platforma backend supports expressions, found: %v",
397
407
  hasExpressionArgs)
398
408
 
399
- if feats.computeLimits {
409
+ if feats.computeLimits || useBatch {
400
410
  if !smart.isReference(_allocation) {
401
411
 
402
412
 
403
- _quota := limits.quotaBuilder().queue(queue).build()
404
- _allocation = limits.computeRequestBuilder().quota(_quota).build().allocation
413
+ _quotaRef := limits.quotaBuilder().queue(queue).build()
414
+ _resReqRef := limits.computeRequestBuilder().queue(queue).quota(_quotaRef).build()
415
+ _allocation = _resReqRef.allocation
405
416
  }
406
- self.getField(_FIELD_ALLOCATION).set(_allocation)
417
+
418
+ runRes.getField(_FIELD_ALLOCATION).set(_allocation)
407
419
  }
408
420
 
409
421
  refsRes := smart.createMapResourceWithType(_RTYPE_RUN_COMMAND_REFS, refs)
410
- self.getField(_FIELD_REFS).set(refsRes)
422
+ runRes.getField(_FIELD_REFS).set(refsRes)
411
423
 
412
424
  cmdRes := smart.createValueResource(_RTYPE_RUN_COMMAND_CMD, json.encode(cmd))
413
- self.getField(_FIELD_CMD).set(cmdRes)
425
+ runRes.getField(_FIELD_CMD).set(cmdRes)
414
426
 
415
427
  options := {
416
428
  queueName: queue,
@@ -423,19 +435,19 @@ builder := func(workdir) {
423
435
  }
424
436
 
425
437
  optionsRes := smart.createValueResource(_RTYPE_RUN_COMMAND_OPTIONS, json.encode(options))
426
- self.getField(_FIELD_OPTIONS).set(optionsRes)
438
+ runRes.getField(_FIELD_OPTIONS).set(optionsRes)
427
439
 
428
440
  argsRes := smart.createValueResource(_RTYPE_RUN_COMMAND_ARGS, json.encode(args))
429
- self.getField(_FIELD_ARGS).set(argsRes)
441
+ runRes.getField(_FIELD_ARGS).set(argsRes)
430
442
 
431
- res := ll.toStrict(oop.inherit(self.lockAndBuild(), {
432
- "workdir": self.outputs()[_FIELD_WORKDIR_OUT],
443
+ res := ll.toStrict(oop.inherit(runRes.lockAndBuild(), {
444
+ "workdir": runRes.outputs()[_FIELD_WORKDIR_OUT],
433
445
  "options": options
434
446
  }))
435
447
 
436
448
  return res
437
449
  }
438
- }))
450
+ })
439
451
 
440
452
  return self
441
453
  }
@@ -18,7 +18,22 @@ _isEnabled := func(featName) {
18
18
  return _checker(featName)
19
19
  }
20
20
 
21
+ _executors := undefined
22
+ _hasRunner := func(e_id) {
23
+ if is_undefined(_executors) {
24
+ _executors = {}
25
+ for e in plapi.getExecutors() {
26
+ _executors[e] = true
27
+ }
28
+ }
29
+
30
+ return !is_undefined(_executors[e_id])
31
+ }
32
+
21
33
  export ll.toStrict({
34
+ hasExec: _hasRunner("executor"), // local binary execution is available
35
+ hasBatch: _hasRunner("batch"), // batch system execution is available
36
+
22
37
  pureFutureFields: _isEnabled("pureFutureFields") || _api >= 1, // 'pure' future fields magic that provides deduplication
23
38
  commandExpressions: _isEnabled("commandExpressions") || _api >= 2, // rich expressions in command arguments ( {{ system.ram_bytes / 20 }} )
24
39
  workdirLimits: _isEnabled("workdirLimits") || _api >= 4, // workdir storage controller can limit concurrency
@@ -135,6 +135,9 @@ isReference := func(f) {
135
135
 
136
136
  _getFutureField := func(from, name, fieldType, isEph) {
137
137
  ll.assert(isReference(from), "'from' must be either a resource or a field, got: %#v", from)
138
+ if is_bytes(name) {
139
+ name = string(name)
140
+ }
138
141
  ll.assert(is_string(name) && name != "", "field name must be a non-empty string")
139
142
 
140
143
  futureField := tx.createFutureFieldID(fieldType, name, !isEph)
@@ -590,6 +593,9 @@ resource = func(r) {
590
593
  isEph = eph[0]
591
594
  }
592
595
 
596
+ if is_bytes(name) {
597
+ name = string(name)
598
+ }
593
599
  ll.assert(is_string(name) && name != "", "field name must be a non-empty string")
594
600
  fieldId := ffDefault.getFutureFieldWithDefault(self.id, name, fieldType, isEph)
595
601
  return field(fieldId)
@@ -1026,6 +1032,9 @@ field = func(f) {
1026
1032
  isEph = eph[0]
1027
1033
  }
1028
1034
 
1035
+ if is_bytes(name) {
1036
+ name = string(name)
1037
+ }
1029
1038
  ll.assert(is_string(name) && name != "", "field name must be a non-empty string")
1030
1039
  fieldId := ffDefault.getFutureFieldWithDefault(self.id, name, fieldType, isEph)
1031
1040
  return field(fieldId)
@@ -1117,6 +1126,9 @@ resourceBuilder = func(r) {
1117
1126
 
1118
1127
  createField: func(name, fieldType) {
1119
1128
  assertNotBuilt()
1129
+ if is_bytes(name) {
1130
+ name = string(name)
1131
+ }
1120
1132
  ll.assert(is_string(name) && name != "", "field name must be a non-empty string")
1121
1133
  fid := ll.fieldId(super.id, name)
1122
1134
  tx.createField(fid, fieldType)
@@ -132,6 +132,9 @@ tpl := func(ops) {
132
132
  _stateRequests := []
133
133
  _delegatedOutputs := []
134
134
 
135
+
136
+
137
+
135
138
  self = {
136
139
 
137
140
 
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/workflow-tengo",
3
- "version": "4.6.4",
3
+ "version": "4.7.0",
4
4
  "description": "Tengo SDK library",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",