base.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. """
  2. The `vecs.experimental.adapter.base` module provides abstract classes and utilities
  3. for creating and handling adapters in vecs. Adapters allow users to interact with
  4. a collection using media types other than vectors.
  5. All public classes, enums, and functions are re-exported by `vecs.adapters` module.
  6. """
  7. from abc import ABC, abstractmethod
  8. from enum import Enum
  9. from typing import Any, Generator, Iterable, Optional, Tuple
  10. from uuid import UUID
  11. from vecs.exc import ArgError
  12. MetadataValues = str | int | float | bool | list[str]
  13. Metadata = dict[str, MetadataValues]
  14. Numeric = int | float | complex
  15. Record = Tuple[
  16. UUID,
  17. UUID,
  18. UUID,
  19. list[UUID],
  20. Iterable[Numeric],
  21. str,
  22. Metadata,
  23. ]
  24. class AdapterContext(str, Enum):
  25. """
  26. An enum representing the different contexts in which a Pipeline
  27. will be invoked.
  28. Attributes:
  29. upsert (str): The Collection.upsert method
  30. query (str): The Collection.query method
  31. """
  32. upsert = "upsert"
  33. query = "query"
  34. class AdapterStep(ABC):
  35. """
  36. Abstract class representing a step in the adapter pipeline.
  37. Each adapter step should adapt a user media into a tuple of:
  38. - id (str)
  39. - media (unknown type)
  40. - metadata (dict)
  41. If the user provides id or metadata, default production is overridden.
  42. """
  43. @property
  44. def exported_dimension(self) -> Optional[int]:
  45. """
  46. Property that should be overridden by subclasses to provide the output dimension
  47. of the adapter step.
  48. """
  49. return None
  50. @abstractmethod
  51. def __call__(
  52. self,
  53. records: Iterable[Tuple[str, Any, Optional[dict]]],
  54. adapter_context: AdapterContext,
  55. ) -> Generator[Tuple[str, Any, dict], None, None]:
  56. """
  57. Abstract method that should be overridden by subclasses to handle each record.
  58. """
  59. class Adapter:
  60. """
  61. Class representing a sequence of AdapterStep instances forming a pipeline.
  62. """
  63. def __init__(self, steps: list[AdapterStep]):
  64. """
  65. Initialize an Adapter instance with a list of AdapterStep instances.
  66. Args:
  67. steps: list of AdapterStep instances.
  68. Raises:
  69. ArgError: Raised if the steps list is empty.
  70. """
  71. self.steps = steps
  72. if len(steps) < 1:
  73. raise ArgError("Adapter must contain at least 1 step")
  74. @property
  75. def exported_dimension(self) -> Optional[int]:
  76. """
  77. The output dimension of the adapter. Returns the exported dimension of the last
  78. AdapterStep that provides one (from end to start of the steps list).
  79. """
  80. for step in reversed(self.steps):
  81. step_dim = step.exported_dimension
  82. if step_dim is not None:
  83. return step_dim
  84. return None
  85. def __call__(
  86. self,
  87. records: Iterable[Tuple[str, Any, Optional[dict]]],
  88. adapter_context: AdapterContext,
  89. ) -> Generator[Tuple[str, Any, dict], None, None]:
  90. """
  91. Invokes the adapter pipeline on an iterable of records.
  92. Args:
  93. records: Iterable of tuples each containing an id, a media and an optional dict.
  94. adapter_context: Context of the adapter.
  95. Yields:
  96. Tuples each containing an id, a media and a dict.
  97. """
  98. pipeline = records
  99. for step in self.steps:
  100. pipeline = step(pipeline, adapter_context)
  101. yield from pipeline # type: ignore