def universal_summary(x, depth=0, max_depth=4, max_items=5, max_chars=350):
    """
    Provides a concise summary of any Python object.
    
    Args:
        x: Any Python object
        depth: Current recursion depth (internal use)
        max_depth: Maximum recursion depth
        max_items: Maximum number of items to show for collections
        max_chars: Maximum characters in the final output
    
    Returns:
        A string summary of the object
    """
    import inspect
    
    # Track the full result and truncate at the end
    result = []
    
    def append(s):
        result.append(str(s))
    
    def get_type_name(obj):
        return type(obj).__name__
    
    def summarize_collection(collection, open_char, close_char):
        if depth >= max_depth:
            append(f"{open_char}...{close_char}")
            return
        
        append(open_char)
        items = list(collection)
        for i, item in enumerate(items[:max_items]):
            if i > 0:
                append(", ")
            universal_summary(item, depth + 1, max_depth, max_items, max_chars)
        if len(items) > max_items:
            append(f", ... ({len(items) - max_items} more)")
        append(close_char)
    
    def summarize_mapping(mapping):
        if depth >= max_depth:
            append("{...}")
            return
        
        append("{")
        items = list(mapping.items())
        for i, (k, v) in enumerate(items[:max_items]):
            if i > 0:
                append(", ")
            universal_summary(k, depth + 1, max_depth, max_items, max_chars)
            append(": ")
            universal_summary(v, depth + 1, max_depth, max_items, max_chars)
        if len(items) > max_items:
            append(f", ... ({len(items) - max_items} more)")
        append("}")
    
    def summarize_class_instance(obj):
        cls = type(obj)
        class_hierarchy = []
        for c in inspect.getmro(cls):
            if c is not object:
                class_hierarchy.append(c.__name__)
        
        append(f"<{' -> '.join(class_hierarchy)} instance")
        
        # Show a few methods
        methods = [name for name, value in inspect.getmembers(obj, inspect.ismethod)
                  if not name.startswith('__')]
        if methods:
            methods_str = ", ".join(methods[:max_items])
            if len(methods) > max_items:
                methods_str += f", ... ({len(methods) - max_items} more)"
            append(f" methods: [{methods_str}]")
        
        # Try to show some attributes
        try:
            attrs = vars(obj)
            if attrs and depth < max_depth:
                append(" attrs: ")
                summarize_mapping(attrs)
        except:
            pass
        
        append(">")
    
    # Handle different types
    if x is None:
        append("None")
    elif isinstance(x, (bool, int, float, str)):
        append(repr(x))
    elif isinstance(x, (list, tuple, set)):
        collection_type = get_type_name(x)
        if isinstance(x, list):
            summarize_collection(x, "[", "]")
        elif isinstance(x, tuple):
            summarize_collection(x, "(", ")")
        elif isinstance(x, set):
            summarize_collection(x, "{", "}")
    elif isinstance(x, dict):
        summarize_mapping(x)
    else:
        # Try to handle special types
        type_name = get_type_name(x)
        
        # NumPy arrays
        if type_name == 'ndarray':
            append(f"ndarray(shape={x.shape}, dtype={x.dtype}")
            if x.size > 0 and depth < max_depth:
                append(", values=[")
                flat_x = x.flatten()
                sample_values = flat_x[:max_items]
                for i, val in enumerate(sample_values):
                    if i > 0:
                        append(", ")
                    append(val)
                if flat_x.size > max_items:
                    append(f", ... ({flat_x.size - max_items} more)")
                append("]")
            append(")")
        
        # PyTorch tensors
        elif 'Tensor' in type_name:
            try:
                shape = tuple(x.shape)
                dtype = str(x.dtype).split(".")[-1]
                append(f"Tensor(shape={shape}, dtype={dtype}")
                if x.numel() > 0 and depth < max_depth:
                    append(", values=[")
                    # Try to convert to numpy first for safer handling
                    try:
                        flat_x = x.detach().cpu().numpy().flatten()
                    except:
                        flat_x = x.flatten().tolist()
                    sample_values = flat_x[:max_items]
                    for i, val in enumerate(sample_values):
                        if i > 0:
                            append(", ")
                        append(val)
                    if len(flat_x) > max_items:
                        append(f", ... ({len(flat_x) - max_items} more)")
                    append("]")
                append(")")
            except:
                append(f"<{type_name} object>")
        
        # Other class instances
        else:
            summarize_class_instance(x)
    
    # Join all parts and truncate if needed
    full_result = "".join(result)
    if len(full_result) > max_chars:
        return full_result[:max_chars-3] + "..."
    return full_result