# Author: Liam Grinstead # SVG tree with tier and overlay labels from lineage_tracker import get_ancestors, get_descendants def _row_positions(n: int, width: int, y: int): if n <= 0: return [] step = width // (n + 1) return [(step * (i + 1), y) for i in range(n)] def render_lineage_tree(agent_id: str, overlay_color_hue: int = 260, max_depth: int = 6) -> str: width, height = 980, 480 # reduced height to avoid cutoff svg = [f""] svg.append("") svg.append(f"RFT Lineage: {agent_id}") root_x, root_y = width // 2, 170 # Ancestors ancestors = get_ancestors(agent_id) if ancestors: pos = _row_positions(len(ancestors), width, 80) for (x, y), aid in zip(pos, ancestors): svg.append(f"") svg.append(f"{aid}") x_last, y_last = pos[-1] svg.append(f"") else: svg.append("No ancestors") # Root agent svg.append( f"" "" "" ) svg.append(f"{agent_id}") # Descendants levels = get_descendants(agent_id, depth=max_depth) y = 260 gap = 90 for d in range(1, max_depth + 1): nodes = levels.get(d, []) if not nodes: break pos = _row_positions(len(nodes), width, y) svg.append(f"Gen {d}") for (x, yy), item in zip(pos, nodes): hue = 40 + (15 * d) child_id = item.get("child_id", "?") tier = item.get("tier", "?") overlay = item.get("overlay", "?") svg.append( f"" "" "" ) label = f"{child_id} · {tier} · {overlay}" svg.append(f"{label}") svg.append( f"" ) y += gap svg.append("") return "".join(svg)