Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Microsoft Autogen Studio Builder Utils

From Leeroopedia
Sources Microsoft_Autogen
Domains Layout Algorithms, Graph Utilities, Position Calculations, Name Generation
Last Updated 2026-02-11 17:00 GMT

Overview

Description

The Studio_Builder_Utils module provides utility functions for managing the AutoGen Studio team builder's graph layout and node positioning. This module implements algorithms for calculating dynamic node positions, handling node dimensions based on content, converting team configurations to graph structures, and generating unique names for components.

Key features include:

  • Dynamic Height Calculation - Calculates node heights based on content (agents, tools, descriptions)
  • Automatic Layout - Positions team and agent nodes with proper spacing
  • Position Persistence - Integrates with layout storage to remember user-positioned nodes
  • Graph Conversion - Converts TeamConfig objects to CustomNode/CustomEdge graph format
  • Name Generation - Creates unique valid identifiers for components

The layout system uses a configuration-based approach with defined spacing and positioning rules that adapt to node content.

Usage

The utilities are used by the team builder store and components to:

  • Initialize graph layout when loading teams
  • Recalculate positions when adding/removing nodes
  • Update node dimensions when content changes
  • Preserve user-customized node positions
  • Generate unique names for new components

Code Reference

Source Location

python/packages/autogen-studio/frontend/src/components/views/teambuilder/builder/utils.ts

Repository: https://github.com/microsoft/autogen

Full path: /tmp/kapso_repo_2mr4n2g4/python/packages/autogen-studio/frontend/src/components/views/teambuilder/builder/utils.ts

Signature

// Position interface
interface Position {
  x: number;
  y: number;
}

// Node dimensions interface
interface NodeDimensions {
  width: number;
  height: number;
}

// Layout configuration constants
const LAYOUT_CONFIG = {
  TEAM_NODE: {
    X_POSITION: 100,
    MIN_Y_POSITION: 200,
  },
  AGENT: {
    START_X: 600,
    START_Y: 200,
    X_STAGGER: 0,
    MIN_Y_STAGGER: 50,
  },
  NODE: {
    WIDTH: 272,
    MIN_HEIGHT: 100,
    PADDING: 20,
  },
  CONTENT_HEIGHTS: {
    BASE: 80,
    DESCRIPTION: 60,
    MODEL_SECTION: 100,
    TOOL_SECTION: 80,
    TOOL_ITEM: 40,
    AGENT_SECTION: 80,
    AGENT_ITEM: 40,
    TERMINATION_SECTION: 80,
  },
}

// Core utility functions
export function convertTeamConfigToGraph(
  teamComponent: Component<TeamConfig>,
  teamId?: string
): { nodes: CustomNode[]; edges: CustomEdge[] }

export function getLayoutedElements(
  nodes: CustomNode[],
  edges: CustomEdge[],
  teamId?: string,
  preserveUserPositions?: boolean
): { nodes: CustomNode[]; edges: CustomEdge[] }

export function updateNodeDimensions(
  nodes: CustomNode[],
  edges: CustomEdge[]
): { nodes: CustomNode[]; edges: CustomEdge[] }

export function getUniqueName(
  baseName: string,
  existingNames: string[]
): string

Import

import {
  convertTeamConfigToGraph,
  getLayoutedElements,
  updateNodeDimensions,
  getUniqueName,
} from './utils';

I/O Contract

Inputs

convertTeamConfigToGraph Parameters:

  • teamComponent - Component<TeamConfig> containing team and agent configurations
  • teamId - Optional string for looking up stored positions

getLayoutedElements Parameters:

  • nodes - CustomNode[] array of existing nodes
  • edges - CustomEdge[] array of existing edges
  • teamId - Optional string for position storage lookup
  • preserveUserPositions - Boolean (default: true) to preserve manually positioned nodes

updateNodeDimensions Parameters:

  • nodes - CustomNode[] array of nodes to update
  • edges - CustomEdge[] array of edges (passed through unchanged)

getUniqueName Parameters:

  • baseName - String to use as base for the name
  • existingNames - String[] array of names already in use

Outputs

convertTeamConfigToGraph Returns:

  • Object with:
    • nodes - CustomNode[] array containing team node and agent nodes
    • edges - CustomEdge[] array of connections from team to agents

getLayoutedElements Returns:

  • Object with:
    • nodes - CustomNode[] array with updated positions and dimensions
    • edges - CustomEdge[] array (unchanged)

updateNodeDimensions Returns:

  • Object with:
    • nodes - CustomNode[] array with updated width/height in data.dimensions
    • edges - CustomEdge[] array (unchanged)

getUniqueName Returns:

  • String - Valid identifier that is unique among existingNames
  • Format: baseName or baseName_N where N is a counter
  • Invalid characters replaced with underscores
  • Starts with letter, underscore, or dollar sign

Layout Behavior

Node Positioning:

  • Team node positioned at x=100, y calculated from average of agent positions
  • Agent nodes positioned at x=600, y calculated cumulatively based on heights
  • Vertical spacing: 50px minimum between agent nodes
  • Node width: Fixed at 272px
  • Node height: Dynamic based on content (100px minimum)

Height Calculation Factors:

  • Base height: 80px for header and basic info
  • Description: +60px if present
  • Team nodes: +80px per agent section, +40px per agent, +80px if termination
  • Agent nodes: +200px for AssistantAgent features, +100px for WebSurfer
  • Workbenches: +80px section header, +40px per tool (static) or +40px for MCP

Usage Examples

Example 1: Converting Team Config to Graph

import { convertTeamConfigToGraph } from './utils';
import { Component, TeamConfig } from '../../../types/datamodel';

// Team configuration with agents
const teamConfig: Component<TeamConfig> = {
  component_type: 'team',
  label: 'My Team',
  provider: 'autogen_agentchat.teams.RoundRobinTeam',
  config: {
    participants: [
      {
        component_type: 'agent',
        label: 'Agent 1',
        provider: 'autogen_agentchat.agents.AssistantAgent',
        config: {
          name: 'agent_1',
          model_client: null,
          tools: [],
        },
      },
    ],
  },
};

// Convert to graph structure
const { nodes, edges } = convertTeamConfigToGraph(teamConfig, 'team-123');

console.log('Created nodes:', nodes.length);
// Team node at (100, 200), Agent node at (600, 200)

console.log('Created edges:', edges.length);
// One edge connecting team to agent

Example 2: Relayouting Nodes After Changes

import { getLayoutedElements } from './utils';

function TeamBuilderCanvas() {
  const [nodes, setNodes] = useState<CustomNode[]>(initialNodes);
  const [edges, setEdges] = useState<CustomEdge[]>(initialEdges);

  const handleRelayout = () => {
    // Recalculate positions, preserving user-positioned nodes
    const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
      nodes,
      edges,
      'team-123',
      true // Preserve user positions
    );

    setNodes(layoutedNodes);
    setEdges(layoutedEdges);
  };

  const handleResetLayout = () => {
    // Force recalculation without preserving positions
    const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
      nodes,
      edges,
      'team-123',
      false // Reset all positions
    );

    setNodes(layoutedNodes);
    setEdges(layoutedEdges);
  };

  return (
    <div>
      <button onClick={handleRelayout}>Relayout Nodes</button>
      <button onClick={handleResetLayout}>Reset Layout</button>
    </div>
  );
}

Example 3: Updating Node Dimensions After Content Changes

import { updateNodeDimensions } from './utils';

function updateAgentTools(
  nodes: CustomNode[],
  edges: CustomEdge[],
  agentNodeId: string,
  newTools: Component<ToolConfig>[]
) {
  // Update agent's tools
  const updatedNodes = nodes.map(node => {
    if (node.id === agentNodeId) {
      return {
        ...node,
        data: {
          ...node.data,
          component: {
            ...node.data.component,
            config: {
              ...node.data.component.config,
              tools: newTools,
            },
          },
        },
      };
    }
    return node;
  });

  // Recalculate dimensions based on new tool count
  const { nodes: dimensionedNodes, edges: dimensionedEdges } =
    updateNodeDimensions(updatedNodes, edges);

  return { nodes: dimensionedNodes, edges: dimensionedEdges };
}

Example 4: Generating Unique Component Names

import { getUniqueName } from './utils';

// Generate unique agent names
const existingAgents = ['agent_1', 'agent_2', 'my_agent'];

const name1 = getUniqueName('agent', existingAgents);
// Returns: 'agent' (not in list)

const name2 = getUniqueName('agent_1', existingAgents);
// Returns: 'agent_1_1' (agent_1 already exists)

const name3 = getUniqueName('my-agent!', existingAgents);
// Returns: 'my_agent_1' (sanitized and made unique)

// Generate unique tool names
const existingTools = ['search_tool', 'calculator'];

const toolName = getUniqueName('search tool', existingTools);
// Returns: 'search_tool_1' (sanitized and made unique)

Example 5: Custom Layout Configuration

import { convertTeamConfigToGraph } from './utils';

// The layout uses predefined constants, but you can understand the positioning:

// Team node position:
// x = 100 (left side)
// y = average of agent Y positions (minimum 200)

// Agent nodes position:
// x = 600 (right side)
// y = cumulative sum of (previous agent heights + 50px spacing)
// Starting y = 200

// Example with 3 agents of heights [300px, 250px, 350px]:
// Agent 1: (600, 200)
// Agent 2: (600, 200 + 300 + 50) = (600, 550)
// Agent 3: (600, 550 + 250 + 50) = (600, 850)
// Team: (100, (200 + 550 + 850) / 3) = (100, 533)

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment