text: from __future__ import annotations
  
  from abc import ABCMeta, abstractmethod
  from collections import UserDict
  from typing import (
      Any,
      Callable,
      Generic,
      Mapping,
      Sequence,
      TypeVar,
      TYPE_CHECKING,
  )
  
  import attr
  
  if TYPE_CHECKING:
      from ai.backend.client.cli.types import CLIContext
  
  
  _predefined_humanized_field_names = {
      "id": "ID",
      "uuid": "UUID",
      "group_id": "Group ID",
      "user_id": "User ID",
      "resource_policy": "Res.Policy",
      "concurrency_limit": "Concur.Limit",
      "concurrency_used": "Concur.Used",
      "fsprefix": "FS Prefix",
      "hardware_metadata": "HW Metadata",
      "performance_metric": "Perf.Metric",
  }
  
  
  def _make_camel_case(name: str) -> str:
      return " ".join(
          map(lambda s: s[0].upper() + s[1:], name.split("_"))
      )
  
  
  class AbstractOutputFormatter(metaclass=ABCMeta):
      """
      The base implementation of output formats.
      """
  
      @abstractmethod
      def format_console(self, value: Any, field: FieldSpec) -> str:
          raise NotImplementedError
  
      @abstractmethod
      def format_json(self, value: Any, field: FieldSpec) -> Any:
          raise NotImplementedError
  
  
  @attr.define(slots=True, frozen=True)
  class FieldSpec:
      """
      The specification on how to represent a GraphQL object field
      in the functional API and CLI output handlers.
  
      Attributes:
          field_ref: The string to be interpolated inside GraphQL queries.
              It may contain sub-fields if the queried field supports.
          humanized_name: The string to be shown as the field name by the console formatter.
              If not set, it's auto-generated from field_name by camel-casing it and checking
              a predefined humanization mapping.
          field_name: The exact field name slug.  If not set, it's taken from field_ref.
          alt_name: The field name slug to refer the field inside a FieldSet object hosting
              this FieldSpec instance.
          formatter: The formatter instance which provide per-output-type format methods.
              (console and json)
          subfields: A FieldSet instance to represent sub-fields in the GraphQL schema.
              If set, field_ref is Automatically updated to have the braced subfield list
              for actual GraphQL queries.
      """
  
      field_ref: str = attr.field()
      humanized_name: str = attr.field()
      field_name: str = attr.field()
      alt_name: str = attr.field()
      formatter: AbstractOutputFormatter = attr.field()
      subfields: FieldSet = attr.field(factory=lambda: FieldSet([]))
  
      def __attrs_post_init__(self) -> None:
          if self.subfields:
              subfields = " ".join(f.field_ref for f in self.subfields.values())
              object.__setattr__(self, 'field_ref', f"{self.field_name} {{ {subfields} }}")
  
      @humanized_name.default
      def _autogen_humanized_name(self) -> str:
          # to handle cases like "groups { id name }", "user_info { full_name }"
          field_name = self.field_ref.partition(" ")[0]
          if h := _predefined_humanized_field_names.get(field_name):
              return h
          if field_name.startswith("is_"):
              return _make_camel_case(field_name[3:]) + "?"
          return _make_camel_case(field_name)
  
      @field_name.default
      def _default_field_name(self) -> str:
          return self.field_ref.partition(" ")[0]
  
      @alt_name.default
      def _default_alt_name(self) -> str:
          return self.field_ref.partition(" ")[0]
  
      @formatter.default
      def _default_formatter(self) -> AbstractOutputFormatter:
          from .formatters import default_output_formatter  # avoid circular import
          return default_output_formatter
  
  
  class FieldSet(UserDict, Mapping[str, FieldSpec]):
  
      def __init__(self, fields: Sequence[FieldSpec]) -> None:
          super().__init__({
              f.alt_name: f for f in fields
          })
  
  
  T = TypeVar('T')
  
  
  @attr.define(slots=True)
  class PaginatedResult(Generic[T]):
      total_count: int
      items: Sequence[T]
      fields: Sequence[FieldSpec]
  
  
  class BaseOutputHandler(metaclass=ABCMeta):
  
      def __init__(self, cli_context: CLIContext) -> None:
          self.ctx = cli_context
  
      @abstractmethod
      def print_item(
          self,
          item: Mapping[str, Any] | None,
          fields: Sequence[FieldSpec],
      ) -> None:
          raise NotImplementedError
  
      @abstractmethod
      def print_items(
          self,
          items: Sequence[Mapping[str, Any]],
          fields: Sequence[FieldSpec],
      ) -> None:
          raise NotImplementedError
  
      @abstractmethod
      def print_list(
          self,
          items: Sequence[Mapping[str, Any]],
          fields: Sequence[FieldSpec],
          *,
          is_scalar: bool = False,
      ) -> None:
          raise NotImplementedError
  
      @abstractmethod
      def print_paginated_list(
          self,
          fetch_func: Callable[[int, int], PaginatedResult[T]],
          initial_page_offset: int,
          page_size: int = None,
      ) -> None:
          raise NotImplementedError
  
      @abstractmethod
      def print_error(
          self,
          error: Exception,
      ) -> None:
          raise NotImplementedError
  
      @abstractmethod
      def print_fail(
          self,
          message: str,
      ) -> None:
          raise NotImplementedError
  