class_docstr: Components orchestration engine.
  
  Builds a graph of components and orchestrates their execution according to the execution graph.
class_docstr_tok: ["Components","orchestration","engine",".","Builds","a","graph","of","components","and","orchestrates","their","execution","according","to","the","execution","graph","."]
docstr: Connects two components together. All components to connect must exist in the pipeline.
  If connecting to an component that has several output connections, specify the inputs and output names as
  'component_name.connections_name'.
  
  Args:
      connect_from: the component that delivers the value. This can be either just a component name or can be
          in the format `component_name.connection_name` if the component has multiple outputs.
      connect_to: the component that receives the value. This can be either just a component name or can be
          in the format `component_name.connection_name` if the component has multiple inputs.
  
  Returns:
      None
  
  Raises:
      PipelineConnectError: if the two components cannot be connected (for example if one of the components is
          not present in the pipeline, or the connections don't match by type, and so on).
docstr_tok: ["Connects","two","components","together",".","All","components","to","connect","must","exist","in","the","pipeline",".","If","connecting","to","an","component","that","has","several","output","connections",",","specify","the","inputs","and","output","names","as","'component_name.connections_name","'",".","Args",":","connect_from",":","the","component","that","delivers","the","value",".","This","can","be","either","just","a","component","name","or","can","be","in","the","format","`","component_name.connection_name","`","if","the","component","has","multiple","outputs",".","connect_to",":","the","component","that","receives","the","value",".","This","can","be","either","just","a","component","name","or","can","be","in","the","format","`","component_name.connection_name","`","if","the","component","has","multiple","inputs",".","Returns",":","None","Raises",":","PipelineConnectError",":","if","the","two","components","can","not","be","connected","(","for","example","if","one","of","the","components","is","not","present","in","the","pipeline",",","or","the","connections","do","n't","match","by","type",",","and","so","on",")","."]
code: def connect(self, connect_from: str, connect_to: str) -> None:
      """
      Connects two components together. All components to connect must exist in the pipeline.
      If connecting to an component that has several output connections, specify the inputs and output names as
      'component_name.connections_name'.
  
      Args:
          connect_from: the component that delivers the value. This can be either just a component name or can be
              in the format `component_name.connection_name` if the component has multiple outputs.
          connect_to: the component that receives the value. This can be either just a component name or can be
              in the format `component_name.connection_name` if the component has multiple inputs.
  
      Returns:
          None
  
      Raises:
          PipelineConnectError: if the two components cannot be connected (for example if one of the components is
              not present in the pipeline, or the connections don't match by type, and so on).
      """
      # Edges may be named explicitly by passing 'node_name.edge_name' to connect().
      sender, sender_socket_name = parse_connect_string(connect_from)
      receiver, receiver_socket_name = parse_connect_string(connect_to)
  
      # Get the nodes data.
      try:
          from_sockets = self.graph.nodes[sender]["output_sockets"]
      except KeyError as exc:
          raise ValueError(
              f"Component named {sender} not found in the pipeline."
          ) from exc
      try:
          to_sockets = self.graph.nodes[receiver]["input_sockets"]
      except KeyError as exc:
          raise ValueError(
              f"Component named {receiver} not found in the pipeline."
          ) from exc
  
      # If the name of either socket is given, get the socket
      sender_socket: Optional[OutputSocket] = None
      if sender_socket_name:
          sender_socket = from_sockets.get(sender_socket_name)
          if not sender_socket:
              raise PipelineConnectError(
                  f"'{connect_from} does not exist. "
                  f"Output connections of {sender} are: "
                  + ", ".join(
                      [
                          f"{name} (type {_type_name(socket.type)})"
                          for name, socket in from_sockets.items()
                      ]
                  )
              )
  
      receiver_socket: Optional[InputSocket] = None
      if receiver_socket_name:
          receiver_socket = to_sockets.get(receiver_socket_name)
          if not receiver_socket:
              raise PipelineConnectError(
                  f"'{connect_to} does not exist. "
                  f"Input connections of {receiver} are: "
                  + ", ".join(
                      [
                          f"{name} (type {_type_name(socket.type)})"
                          for name, socket in to_sockets.items()
                      ]
                  )
              )
  
      # Look for a matching connection among the possible ones.
      # Note that if there is more than one possible connection but two sockets match by name, they're paired.
      sender_socket_candidates: List[OutputSocket] = (
          [sender_socket]
          if sender_socket
          else list(from_sockets.values())
      )
      receiver_socket_candidates: List[InputSocket] = (
          [receiver_socket]
          if receiver_socket
          else list(to_sockets.values())
      )
  
      connection = Connection.from_list_of_sockets(
          sender,
          sender_socket_candidates,
          receiver,
          receiver_socket_candidates,
      )
  
      # Connection must be valid on both sender/receiver sides
      if (
          not connection.sender_socket
          or not connection.receiver_socket
          or not connection.sender
          or not connection.receiver
      ):
          raise PipelineConnectError(
              "Connection must have both sender and receiver: {connection}"
          )
  
      # Create the connection
      logger.debug(
          "Connecting '%s.%s' to '%s.%s'",
          connection.sender,
          connection.sender_socket.name,
          connection.receiver,
          connection.receiver_socket.name,
      )
  
      self.graph.add_edge(
          connection.sender,
          connection.receiver,
          key=f"{connection.sender_socket.name}/{connection.receiver_socket.name}",
          conn_type=_type_name(connection.sender_socket.type),
          from_socket=connection.sender_socket,
          to_socket=connection.receiver_socket,
          mandatory=connection.is_mandatory,
      )
  
      self._connections.append(connection)
      if connection.is_mandatory:
          self._mandatory_connections[connection.receiver].append(
              connection
          )
  
imports: from typing import Optional, Any, Dict, List, Union, TypeVar, Type, Set
  import os
  import json
  import datetime
  import logging
  import importlib
  from pathlib import Path
  from copy import deepcopy
  from collections import defaultdict
  import networkx
  from haystack.core.component import component, Component, InputSocket, OutputSocket
  from haystack.core.errors import PipelineError, PipelineConnectError, PipelineMaxLoops, PipelineRuntimeError, PipelineValidationError
  from haystack.core.pipeline.descriptions import find_pipeline_outputs
  from haystack.core.pipeline.draw.draw import _draw, RenderingEngines
  from haystack.core.pipeline.validation import validate_pipeline_input, find_pipeline_inputs
  from haystack.core.component.connection import Connection, parse_connect_string
  from haystack.core.type_utils import _type_name
  from haystack.core.serialization import component_to_dict, component_from_dict
label_desc: Use image node_id 7 for calling the Pipeline obj's underlying member method code with example usage: obj.connect(connect_from, connect_to) without return types