@@ -12,15 +12,14 @@ render := import("@platforma-sdk/workflow-tengo:render")
1212smart := import("@platforma-sdk/workflow-tengo:smart")
1313ll := import("@platforma-sdk/workflow-tengo:ll")
1414pframes := import("@platforma-sdk/workflow-tengo:pframes")
15- xsv := import("@platforma-sdk/workflow-tengo:pframes.xsv")
16- pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
1715canonical := import("@platforma-sdk/workflow-tengo:canonical")
1816maps := import("@platforma-sdk/workflow-tengo:maps")
1917constants := import("@platforma-sdk/workflow-tengo:constants")
2018messages := import(":messages")
2119columnSpecs := import(":columns")
2220
2321infoTpl := assets.importTemplate(":info")
22+ processTpl := assets.importTemplate(":process")
2423
2524// JSON resource with sorted-key canonical bytes. smart.createJsonResource uses
2625// Tengo's stdlib json.encode, which preserves Go's randomized map iteration —
@@ -31,21 +30,6 @@ canonicalJsonResource := func(value) {
3130 return smart.createValueResource(constants.RTYPE_JSON, canonical.encode(value))
3231}
3332
34- // Content tag for exported columns, replacing the per-block blockId. We use the
35- // backend's own content key of the source table (info().CanonicalID, base32),
36- // which is deterministic for identical content: identical results across blocks
37- // share the same tag (columns dedupe instead of being made unique per block),
38- // while different results get different tags (no collision).
39- contentHash := func(table) {
40- i := table.info()
41- if !is_undefined(i.CanonicalID) {
42- return ll.base32Encode(i.CanonicalID)
43- }
44- // Field reference: dereference to its value resource.
45- ri := smart.resource(i.Value).info()
46- return ll.base32Encode(ri.CanonicalID)
47- }
48-
4933REQUIRED_FEATURES := ["FR1", "CDR1", "FR2", "CDR2", "FR3", "CDR3", "FR4"]
5034
5135detectMode := func(axisSpec) {
@@ -408,10 +392,8 @@ wf.body(func(args) {
408392 })
409393 infoBlob := infoResult.output("info", 24 * 60 * 60 * 1000)
410394
411- // Build the canonical column list (no blockId, no isOutput, no trace) and
412- // import the Python TSV outputs into parquet-backed PColumn data. With
413- // canonical specs the xsv.importFile renders dedup across blocks; only
414- // the spec-rewrap into the unified pFrame below is per-block.
395+ // Column-build args forwarded to the process template (no blockId, no trace
396+ // — those are stamped there at pframe-build time).
415397 colArgs := {
416398 mode: mode,
417399 receptor: receptor,
@@ -426,72 +408,34 @@ wf.body(func(args) {
426408 visibleKeyAxisSpec := columnSpecs.cloneSpec(keyAxisSpec, undefined, {
427409 "pl7.app/table/visibility": "default"
428410 })
429- canonicalCols := columnSpecs.buildColumns(colArgs)
430- scalarOut := xsv.importFile(propertiesTsv, "tsv", {
431- axes: [{ column: "entity_key", spec: visibleKeyAxisSpec }],
432- columns: canonicalCols,
433- storageFormat: "Parquet",
434- partitionKeyLength: 0
435- }, { splitDataAndSpec: true, cpu: 1, mem: "4GiB" })
436-
437- aaOut := undefined
438- if mode == "peptide" {
439- aaCol := columnSpecs.aaFractionColumn(visibleKeyAxisSpec)
440- aaOut = xsv.importFile(aaFractionTsv, "tsv", {
441- axes: aaCol.axes,
442- columns: [aaCol.column],
443- storageFormat: "Parquet",
444- partitionKeyLength: 0
445- }, { splitDataAndSpec: true, cpu: 1, mem: "4GiB" })
446- }
447411
448- // Stamp every column's spec with a content tag (pl7.app/contentHash, derived
449- // from the source table's CanonicalID instead of the per-block blockId) and
450- // the upstream trace. Identical results across blocks share the tag (columns
451- // dedupe) while different results stay distinct. The same pFrame is published
452- // both as the block's UI output and as the result-pool export. The trace also
453- // lets the block's UI distinguish our property columns from upstream metadata
454- // columns mixed into propertiesPfHandle.
455- //
456- // The trace step is no longer keyed on blockId (the block is no longer the
457- // identity of the result; its content is). `id` is optional in the trace
458- // schema; omitting it keeps the provenance step identical across
459- // content-identical runs. Per-instance trace label resolution lives in the
460- // model (resolveTraceLabel in model/src/label.ts).
461- trace := pSpec.makeTrace( datasetSpec, {
462- type: "milaboratories.sequence-properties",
463- importance: 30,
464- label: args.traceLabel
412+ // Import + per- column content-tagging + trace stamping is deferred to
413+ // process.tpl.tengo: it needs the Python output tables as RESOLVED render
414+ // inputs to read their content key (info().CanonicalID), which is not
415+ // available in this body (the same body that creates the exec). The render
416+ // is blockId-independent, so it dedups across block instances with identical
417+ // inputs.
418+ processResult := render.create(processTpl, {
419+ properties: propertiesTsv,
420+ aaFraction: aaFractionTsv,
421+ params: canonicalJsonResource({
422+ colArgs: colArgs,
423+ visibleKeyAxisSpec: visibleKeyAxisSpec,
424+ traceLabel: args.traceLabel,
425+ datasetSpec: datasetSpec
426+ })
427+ }, {
428+ metaInputs: { mem: args.mem }
465429 })
466- stamp := func(spec, cHash) {
467- return trace.inject(columnSpecs.cloneSpec(spec,
468- { "pl7.app/contentHash": cHash },
469- undefined))
470- }
471-
472- pf := pframes.pFrameBuilder()
473- scalarHash := contentHash(propertiesTsv)
474- for _, k in maps.getKeys(scalarOut) {
475- v := scalarOut[k]
476- pf.add(k, stamp(v.spec, scalarHash), v.data)
477- }
478- if aaOut != undefined {
479- aaHash := contentHash(aaFractionTsv)
480- for _, k in maps.getKeys(aaOut) {
481- v := aaOut[k]
482- pf.add(k, stamp(v.spec, aaHash), v.data)
483- }
484- }
485- pf = pf.build()
486430
487431 return {
488432 outputs: {
489- propertiesPf: pframes.exportFrame(pf ),
433+ propertiesPf: processResult.output("propertiesPf", 24 * 60 * 60 * 1000 ),
490434 info: infoBlob,
491435 processingLog: processingLog
492436 },
493437 exports: {
494- properties: pf
438+ properties: processResult.output("exportPframe", 24 * 60 * 60 * 1000)
495439 }
496440 }
497441})
0 commit comments