auto-sync 2026-07-04T07:48:09Z workspace (part 4)
Browse files
workspace/scripts/build_selector_diagnostic_sweep.py
CHANGED
|
@@ -18,6 +18,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
| 18 |
|
| 19 |
DEFAULT_PATTERNS = (
|
| 20 |
"runs/ctt_base_context_obs_learned_dominance_chartcompat_obs_utility_task_envclip_k16_train_to_test/metrics.json",
|
|
|
|
| 21 |
"runs/ctt_base_context_obs_dominance_envclip_k16_train_to_test/metrics.json",
|
| 22 |
"runs/ctt_base_context_obs_dominance_envclip_k16_train_to_test_tau0/metrics.json",
|
| 23 |
"runs/ctt_dominance_utility_energy_val_to_test_seed*/metrics.json",
|
|
|
|
| 18 |
|
| 19 |
DEFAULT_PATTERNS = (
|
| 20 |
"runs/ctt_base_context_obs_learned_dominance_chartcompat_obs_utility_task_envclip_k16_train_to_test/metrics.json",
|
| 21 |
+
"runs/ctt_base_context_obs_learned_dominance_*bundle*_envclip_k16_train_to_test/metrics.json",
|
| 22 |
"runs/ctt_base_context_obs_dominance_envclip_k16_train_to_test/metrics.json",
|
| 23 |
"runs/ctt_base_context_obs_dominance_envclip_k16_train_to_test_tau0/metrics.json",
|
| 24 |
"runs/ctt_dominance_utility_energy_val_to_test_seed*/metrics.json",
|
workspace/scripts/eval_learned_dominance_selector.py
CHANGED
|
@@ -92,12 +92,37 @@ SCORE_SHAPE_NAMES = [
|
|
| 92 |
"candidate_score_percentile",
|
| 93 |
"candidate_score_top_margin",
|
| 94 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
FEATURE_SET_CHOICES = (
|
| 96 |
"basic",
|
| 97 |
"tangent",
|
| 98 |
"context",
|
| 99 |
"context_tangent",
|
| 100 |
"score_context",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
"source_evidence",
|
| 102 |
"tangent_source_evidence",
|
| 103 |
"context_source_evidence",
|
|
@@ -106,8 +131,11 @@ FEATURE_SET_CHOICES = (
|
|
| 106 |
"chart_tangent_compat",
|
| 107 |
"score_chart_compat",
|
| 108 |
"score_context_chart_compat",
|
|
|
|
|
|
|
| 109 |
"chart_source_compat",
|
| 110 |
"chart_tangent_source_compat",
|
|
|
|
| 111 |
)
|
| 112 |
|
| 113 |
|
|
@@ -456,6 +484,7 @@ def _candidate_dataset(
|
|
| 456 |
score_mean = sum(scores) / len(scores)
|
| 457 |
score_std = math.sqrt(sum((score - score_mean) ** 2 for score in scores) / len(scores)) + 1.0e-6
|
| 458 |
score_shape = _score_shape_matrix(scores)
|
|
|
|
| 459 |
for candidate_index, score in enumerate(scores):
|
| 460 |
source_chart_id = str(source_chart_ids[candidate_index]) if candidate_index < len(source_chart_ids) else ""
|
| 461 |
tangent = np.asarray(
|
|
@@ -489,6 +518,7 @@ def _candidate_dataset(
|
|
| 489 |
chart_feature_mode=selector_chart_feature_mode,
|
| 490 |
),
|
| 491 |
score_shape=score_shape[candidate_index],
|
|
|
|
| 492 |
num_candidates=len(scores),
|
| 493 |
feature_set=feature_set,
|
| 494 |
)
|
|
@@ -554,6 +584,8 @@ def _feature_names(feature_set: str) -> list[str]:
|
|
| 554 |
names.extend(SOURCE_EVIDENCE_NAMES)
|
| 555 |
if _uses_chart_compat(feature_set):
|
| 556 |
names.extend(CHART_COMPAT_NAMES)
|
|
|
|
|
|
|
| 557 |
if feature_set in FEATURE_SET_CHOICES:
|
| 558 |
return names
|
| 559 |
raise ValueError(f"unknown feature_set: {feature_set}")
|
|
@@ -574,6 +606,7 @@ def _candidate_feature(
|
|
| 574 |
source_evidence: np.ndarray | None = None,
|
| 575 |
chart_compat: np.ndarray | None = None,
|
| 576 |
score_shape: np.ndarray | None = None,
|
|
|
|
| 577 |
) -> np.ndarray:
|
| 578 |
tangent = np.asarray(tangent, dtype=float).reshape(-1)
|
| 579 |
if tangent.size < 21:
|
|
@@ -616,6 +649,10 @@ def _candidate_feature(
|
|
| 616 |
if chart_compat is None:
|
| 617 |
chart_compat = np.zeros(len(CHART_COMPAT_NAMES), dtype=float)
|
| 618 |
parts.append(np.asarray(chart_compat, dtype=float).reshape(-1))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
if feature_set in FEATURE_SET_CHOICES:
|
| 620 |
return np.concatenate(parts)
|
| 621 |
raise ValueError(f"unknown feature_set: {feature_set}")
|
|
@@ -629,6 +666,8 @@ def _uses_context(feature_set: str) -> bool:
|
|
| 629 |
"context_tangent_source_evidence",
|
| 630 |
"score_context",
|
| 631 |
"score_context_chart_compat",
|
|
|
|
|
|
|
| 632 |
}
|
| 633 |
|
| 634 |
|
|
@@ -637,6 +676,8 @@ def _uses_score_shape(feature_set: str) -> bool:
|
|
| 637 |
"score_context",
|
| 638 |
"score_chart_compat",
|
| 639 |
"score_context_chart_compat",
|
|
|
|
|
|
|
| 640 |
}
|
| 641 |
|
| 642 |
|
|
@@ -648,6 +689,7 @@ def _uses_tangent(feature_set: str) -> bool:
|
|
| 648 |
"context_tangent_source_evidence",
|
| 649 |
"chart_tangent_compat",
|
| 650 |
"chart_tangent_source_compat",
|
|
|
|
| 651 |
}
|
| 652 |
|
| 653 |
|
|
@@ -659,6 +701,7 @@ def _uses_source_evidence(feature_set: str) -> bool:
|
|
| 659 |
"context_tangent_source_evidence",
|
| 660 |
"chart_source_compat",
|
| 661 |
"chart_tangent_source_compat",
|
|
|
|
| 662 |
}
|
| 663 |
|
| 664 |
|
|
@@ -668,8 +711,23 @@ def _uses_chart_compat(feature_set: str) -> bool:
|
|
| 668 |
"chart_tangent_compat",
|
| 669 |
"score_chart_compat",
|
| 670 |
"score_context_chart_compat",
|
|
|
|
|
|
|
| 671 |
"chart_source_compat",
|
| 672 |
"chart_tangent_source_compat",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 673 |
}
|
| 674 |
|
| 675 |
|
|
@@ -957,6 +1015,111 @@ def _score_shape_matrix(scores: list[float]) -> np.ndarray:
|
|
| 957 |
return np.asarray(rows, dtype=float)
|
| 958 |
|
| 959 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 960 |
def _context_feature(context: dict[str, Any]) -> np.ndarray:
|
| 961 |
target_task = str(context.get("target_task_id", ""))
|
| 962 |
source_task = str(context.get("source_task_id", ""))
|
|
|
|
| 92 |
"candidate_score_percentile",
|
| 93 |
"candidate_score_top_margin",
|
| 94 |
]
|
| 95 |
+
BUNDLE_CONSENSUS_NAMES = [
|
| 96 |
+
"bundle_num_candidates_log",
|
| 97 |
+
"bundle_neighbor_count_r020",
|
| 98 |
+
"bundle_neighbor_count_r040",
|
| 99 |
+
"bundle_neighbor_frac_r020",
|
| 100 |
+
"bundle_neighbor_frac_r040",
|
| 101 |
+
"bundle_mean_peer_rms",
|
| 102 |
+
"bundle_min_peer_rms",
|
| 103 |
+
"bundle_medoid_rms",
|
| 104 |
+
"bundle_is_medoid",
|
| 105 |
+
"bundle_peer_score_mean_r020",
|
| 106 |
+
"bundle_peer_score_max_r020",
|
| 107 |
+
"bundle_peer_score_mean_r040",
|
| 108 |
+
"bundle_peer_score_max_r040",
|
| 109 |
+
"bundle_unique_source_count_r040",
|
| 110 |
+
"bundle_unique_source_frac_r040",
|
| 111 |
+
"bundle_unique_task_count_r040",
|
| 112 |
+
"bundle_same_source_frac_r040",
|
| 113 |
+
"bundle_score_density_r040",
|
| 114 |
+
"bundle_rank_density_r040",
|
| 115 |
+
]
|
| 116 |
FEATURE_SET_CHOICES = (
|
| 117 |
"basic",
|
| 118 |
"tangent",
|
| 119 |
"context",
|
| 120 |
"context_tangent",
|
| 121 |
"score_context",
|
| 122 |
+
"bundle_consensus",
|
| 123 |
+
"score_bundle_consensus",
|
| 124 |
+
"context_bundle_consensus",
|
| 125 |
+
"context_tangent_bundle_consensus",
|
| 126 |
"source_evidence",
|
| 127 |
"tangent_source_evidence",
|
| 128 |
"context_source_evidence",
|
|
|
|
| 131 |
"chart_tangent_compat",
|
| 132 |
"score_chart_compat",
|
| 133 |
"score_context_chart_compat",
|
| 134 |
+
"chart_bundle_consensus",
|
| 135 |
+
"score_chart_bundle_consensus",
|
| 136 |
"chart_source_compat",
|
| 137 |
"chart_tangent_source_compat",
|
| 138 |
+
"chart_source_bundle_consensus",
|
| 139 |
)
|
| 140 |
|
| 141 |
|
|
|
|
| 484 |
score_mean = sum(scores) / len(scores)
|
| 485 |
score_std = math.sqrt(sum((score - score_mean) ** 2 for score in scores) / len(scores)) + 1.0e-6
|
| 486 |
score_shape = _score_shape_matrix(scores)
|
| 487 |
+
bundle_consensus = _bundle_consensus_matrix(tangents, scores, source_chart_ids, source_task_ids)
|
| 488 |
for candidate_index, score in enumerate(scores):
|
| 489 |
source_chart_id = str(source_chart_ids[candidate_index]) if candidate_index < len(source_chart_ids) else ""
|
| 490 |
tangent = np.asarray(
|
|
|
|
| 518 |
chart_feature_mode=selector_chart_feature_mode,
|
| 519 |
),
|
| 520 |
score_shape=score_shape[candidate_index],
|
| 521 |
+
bundle_consensus=bundle_consensus[candidate_index],
|
| 522 |
num_candidates=len(scores),
|
| 523 |
feature_set=feature_set,
|
| 524 |
)
|
|
|
|
| 584 |
names.extend(SOURCE_EVIDENCE_NAMES)
|
| 585 |
if _uses_chart_compat(feature_set):
|
| 586 |
names.extend(CHART_COMPAT_NAMES)
|
| 587 |
+
if _uses_bundle_consensus(feature_set):
|
| 588 |
+
names.extend(BUNDLE_CONSENSUS_NAMES)
|
| 589 |
if feature_set in FEATURE_SET_CHOICES:
|
| 590 |
return names
|
| 591 |
raise ValueError(f"unknown feature_set: {feature_set}")
|
|
|
|
| 606 |
source_evidence: np.ndarray | None = None,
|
| 607 |
chart_compat: np.ndarray | None = None,
|
| 608 |
score_shape: np.ndarray | None = None,
|
| 609 |
+
bundle_consensus: np.ndarray | None = None,
|
| 610 |
) -> np.ndarray:
|
| 611 |
tangent = np.asarray(tangent, dtype=float).reshape(-1)
|
| 612 |
if tangent.size < 21:
|
|
|
|
| 649 |
if chart_compat is None:
|
| 650 |
chart_compat = np.zeros(len(CHART_COMPAT_NAMES), dtype=float)
|
| 651 |
parts.append(np.asarray(chart_compat, dtype=float).reshape(-1))
|
| 652 |
+
if _uses_bundle_consensus(feature_set):
|
| 653 |
+
if bundle_consensus is None:
|
| 654 |
+
bundle_consensus = np.zeros(len(BUNDLE_CONSENSUS_NAMES), dtype=float)
|
| 655 |
+
parts.append(np.asarray(bundle_consensus, dtype=float).reshape(-1))
|
| 656 |
if feature_set in FEATURE_SET_CHOICES:
|
| 657 |
return np.concatenate(parts)
|
| 658 |
raise ValueError(f"unknown feature_set: {feature_set}")
|
|
|
|
| 666 |
"context_tangent_source_evidence",
|
| 667 |
"score_context",
|
| 668 |
"score_context_chart_compat",
|
| 669 |
+
"context_bundle_consensus",
|
| 670 |
+
"context_tangent_bundle_consensus",
|
| 671 |
}
|
| 672 |
|
| 673 |
|
|
|
|
| 676 |
"score_context",
|
| 677 |
"score_chart_compat",
|
| 678 |
"score_context_chart_compat",
|
| 679 |
+
"score_bundle_consensus",
|
| 680 |
+
"score_chart_bundle_consensus",
|
| 681 |
}
|
| 682 |
|
| 683 |
|
|
|
|
| 689 |
"context_tangent_source_evidence",
|
| 690 |
"chart_tangent_compat",
|
| 691 |
"chart_tangent_source_compat",
|
| 692 |
+
"context_tangent_bundle_consensus",
|
| 693 |
}
|
| 694 |
|
| 695 |
|
|
|
|
| 701 |
"context_tangent_source_evidence",
|
| 702 |
"chart_source_compat",
|
| 703 |
"chart_tangent_source_compat",
|
| 704 |
+
"chart_source_bundle_consensus",
|
| 705 |
}
|
| 706 |
|
| 707 |
|
|
|
|
| 711 |
"chart_tangent_compat",
|
| 712 |
"score_chart_compat",
|
| 713 |
"score_context_chart_compat",
|
| 714 |
+
"chart_bundle_consensus",
|
| 715 |
+
"score_chart_bundle_consensus",
|
| 716 |
"chart_source_compat",
|
| 717 |
"chart_tangent_source_compat",
|
| 718 |
+
"chart_source_bundle_consensus",
|
| 719 |
+
}
|
| 720 |
+
|
| 721 |
+
|
| 722 |
+
def _uses_bundle_consensus(feature_set: str) -> bool:
|
| 723 |
+
return feature_set in {
|
| 724 |
+
"bundle_consensus",
|
| 725 |
+
"score_bundle_consensus",
|
| 726 |
+
"context_bundle_consensus",
|
| 727 |
+
"context_tangent_bundle_consensus",
|
| 728 |
+
"chart_bundle_consensus",
|
| 729 |
+
"score_chart_bundle_consensus",
|
| 730 |
+
"chart_source_bundle_consensus",
|
| 731 |
}
|
| 732 |
|
| 733 |
|
|
|
|
| 1015 |
return np.asarray(rows, dtype=float)
|
| 1016 |
|
| 1017 |
|
| 1018 |
+
def _bundle_consensus_matrix(
|
| 1019 |
+
tangents: Any,
|
| 1020 |
+
scores: list[float],
|
| 1021 |
+
source_chart_ids: Any,
|
| 1022 |
+
source_task_ids: Any,
|
| 1023 |
+
) -> np.ndarray:
|
| 1024 |
+
"""Deployment-visible CTT bundle self-consistency features.
|
| 1025 |
+
|
| 1026 |
+
These features are computed only from the generated transported tangents,
|
| 1027 |
+
their inference-time scores, and train-source identifiers already present
|
| 1028 |
+
in the candidate row. They deliberately do not inspect target positive or
|
| 1029 |
+
negative tangent sets, measured candidate utilities, or hidden outcomes.
|
| 1030 |
+
"""
|
| 1031 |
+
|
| 1032 |
+
num_candidates = len(scores)
|
| 1033 |
+
if num_candidates == 0:
|
| 1034 |
+
return np.zeros((0, len(BUNDLE_CONSENSUS_NAMES)), dtype=float)
|
| 1035 |
+
tangent_matrix = _candidate_tangent_matrix(tangents, num_candidates)
|
| 1036 |
+
score_array = np.asarray(scores, dtype=float).reshape(-1)
|
| 1037 |
+
if score_array.size < num_candidates:
|
| 1038 |
+
score_array = np.pad(score_array, (0, num_candidates - score_array.size))
|
| 1039 |
+
score_array = score_array[:num_candidates]
|
| 1040 |
+
source_ids = _string_list(source_chart_ids, num_candidates)
|
| 1041 |
+
task_ids = _string_list(source_task_ids, num_candidates)
|
| 1042 |
+
|
| 1043 |
+
diff = tangent_matrix[:, None, :] - tangent_matrix[None, :, :]
|
| 1044 |
+
distances = np.sqrt(np.mean(diff * diff, axis=2))
|
| 1045 |
+
nonself = ~np.eye(num_candidates, dtype=bool)
|
| 1046 |
+
peer_denominator = max(1.0, float(num_candidates - 1))
|
| 1047 |
+
peer_distances = np.where(nonself, distances, np.nan)
|
| 1048 |
+
mean_peer = np.nanmean(peer_distances, axis=1) if num_candidates > 1 else np.zeros(num_candidates)
|
| 1049 |
+
min_peer = np.nanmin(peer_distances, axis=1) if num_candidates > 1 else np.zeros(num_candidates)
|
| 1050 |
+
mean_peer = np.nan_to_num(mean_peer, nan=0.0, posinf=0.0, neginf=0.0)
|
| 1051 |
+
min_peer = np.nan_to_num(min_peer, nan=0.0, posinf=0.0, neginf=0.0)
|
| 1052 |
+
medoid_index = int(np.argmin(mean_peer)) if num_candidates else 0
|
| 1053 |
+
medoid_distances = distances[:, medoid_index] if num_candidates else np.zeros(0)
|
| 1054 |
+
|
| 1055 |
+
rows: list[list[float]] = []
|
| 1056 |
+
for index in range(num_candidates):
|
| 1057 |
+
peers_020 = [j for j in range(num_candidates) if j != index and distances[index, j] <= 0.20]
|
| 1058 |
+
peers_040 = [j for j in range(num_candidates) if j != index and distances[index, j] <= 0.40]
|
| 1059 |
+
group_040 = [index, *peers_040]
|
| 1060 |
+
unique_sources = {source_ids[j] for j in group_040 if source_ids[j]}
|
| 1061 |
+
unique_tasks = {task_ids[j] for j in group_040 if task_ids[j]}
|
| 1062 |
+
same_source_peers = [
|
| 1063 |
+
j for j in peers_040 if source_ids[index] and source_ids[j] == source_ids[index]
|
| 1064 |
+
]
|
| 1065 |
+
score_mean_020, score_max_020 = _score_stats(score_array, peers_020)
|
| 1066 |
+
score_mean_040, score_max_040 = _score_stats(score_array, peers_040)
|
| 1067 |
+
density_weights = np.exp(-np.clip(distances[index], 0.0, 10.0) / 0.40)
|
| 1068 |
+
density_weights[index] = 0.0
|
| 1069 |
+
score_density = float(np.dot(density_weights, score_array) / max(1.0e-12, density_weights.sum()))
|
| 1070 |
+
rank_density = float(sum(1.0 for j in peers_040 if score_array[j] >= score_array[index]))
|
| 1071 |
+
rows.append(
|
| 1072 |
+
[
|
| 1073 |
+
math.log1p(num_candidates),
|
| 1074 |
+
float(len(peers_020)),
|
| 1075 |
+
float(len(peers_040)),
|
| 1076 |
+
float(len(peers_020)) / peer_denominator,
|
| 1077 |
+
float(len(peers_040)) / peer_denominator,
|
| 1078 |
+
float(mean_peer[index]),
|
| 1079 |
+
float(min_peer[index]),
|
| 1080 |
+
float(medoid_distances[index]),
|
| 1081 |
+
float(index == medoid_index),
|
| 1082 |
+
score_mean_020,
|
| 1083 |
+
score_max_020,
|
| 1084 |
+
score_mean_040,
|
| 1085 |
+
score_max_040,
|
| 1086 |
+
float(len(unique_sources)),
|
| 1087 |
+
float(len(unique_sources)) / max(1.0, float(len(group_040))),
|
| 1088 |
+
float(len(unique_tasks)),
|
| 1089 |
+
float(len(same_source_peers)) / max(1.0, float(len(peers_040))),
|
| 1090 |
+
score_density,
|
| 1091 |
+
rank_density / max(1.0, float(len(peers_040))),
|
| 1092 |
+
]
|
| 1093 |
+
)
|
| 1094 |
+
output = np.asarray(rows, dtype=float)
|
| 1095 |
+
return np.nan_to_num(output, nan=0.0, posinf=0.0, neginf=0.0)
|
| 1096 |
+
|
| 1097 |
+
|
| 1098 |
+
def _candidate_tangent_matrix(tangents: Any, num_candidates: int) -> np.ndarray:
|
| 1099 |
+
rows: list[np.ndarray] = []
|
| 1100 |
+
tangent_list = list(tangents or [])
|
| 1101 |
+
for index in range(num_candidates):
|
| 1102 |
+
tangent = np.asarray(tangent_list[index] if index < len(tangent_list) else [], dtype=float).reshape(-1)
|
| 1103 |
+
if tangent.size < 21:
|
| 1104 |
+
tangent = np.pad(tangent, (0, 21 - tangent.size))
|
| 1105 |
+
elif tangent.size > 21:
|
| 1106 |
+
tangent = tangent[:21]
|
| 1107 |
+
rows.append(tangent.astype(float, copy=False))
|
| 1108 |
+
return np.stack(rows, axis=0)
|
| 1109 |
+
|
| 1110 |
+
|
| 1111 |
+
def _string_list(values: Any, length: int) -> list[str]:
|
| 1112 |
+
raw = list(values or [])
|
| 1113 |
+
return [str(raw[index]) if index < len(raw) else "" for index in range(length)]
|
| 1114 |
+
|
| 1115 |
+
|
| 1116 |
+
def _score_stats(scores: np.ndarray, indices: list[int]) -> tuple[float, float]:
|
| 1117 |
+
if not indices:
|
| 1118 |
+
return 0.0, 0.0
|
| 1119 |
+
values = scores[indices]
|
| 1120 |
+
return float(values.mean()), float(values.max())
|
| 1121 |
+
|
| 1122 |
+
|
| 1123 |
def _context_feature(context: dict[str, Any]) -> np.ndarray:
|
| 1124 |
target_task = str(context.get("target_task_id", ""))
|
| 1125 |
source_task = str(context.get("source_task_id", ""))
|