code: def lower_schedule_item(si: ScheduleItem) -> Optional[JITRunner]:
      assert (
          all(si.out.device == x.device for x in si.inputs)
          or si.ast.op is LoadOps.COPY
      ), f"all devices must be the same, {si.out.device} != {[x.device for x in si.inputs]} {print_tree(si.ast) or ''}"
      if si.ast.op is LoadOps.EMPTY:
          return None
      if si.ast.op is LoadOps.COPY:
          return BufferCopy
      if si.ast.op is LoadOps.CUSTOM:
          return CustomOp(si.ast.arg)
      return Device[si.out.device].get_runner(si.ast)
  
file_path: tinygrad/tinygrad/realize.py
imports: from typing import List, Dict, Optional, cast
  from tinygrad.ops import LoadOps, ScheduleItem, BufferOps, GlobalCounters
  from tinygrad.device import Device, Buffer, BufferCopy, JITRunner, update_stats, InterpretedASTRunner
  from tinygrad.graph import print_tree, realized_lazybuffer
  from tinygrad.helpers import colored, getenv, GRAPH
  from tinygrad.shape.symbolic import Variable
label_desc: Use image node_id 1 for calling a global function with example usage: lower_schedule_item(si) and returns: Device, None, BufferCopy, CustomOp