hojichar.core.async_composition
1from __future__ import annotations 2 3import asyncio 4import logging 5from concurrent.futures import Executor, ThreadPoolExecutor 6from typing import Any, AsyncGenerator, AsyncIterable, Iterable, Sequence 7 8import numpy as np 9 10from hojichar.core import inspection 11from hojichar.core.async_filter_interface import AsyncFilter 12from hojichar.core.composition import Compose 13from hojichar.core.filter_interface import Filter 14from hojichar.core.models import Document, Statistics, get_doc_info 15from hojichar.utils.async_handlers import ( 16 AsyncToSyncIterator, 17 handle_async_stream_as_sync, 18 handle_stream_as_async, 19) 20 21 22class AsyncFilterAdapter(AsyncFilter): 23 """ 24 Adapter class for executing hojichar.Filter asynchronously. 25 """ 26 27 def __init__( 28 self, 29 sync_filter: Filter, 30 *args: Any, 31 executor: Executor | None = None, 32 use_batch: bool = True, 33 **kwargs: Any, 34 ): 35 """ 36 Adapter class for executing hojichar.Filter asynchronously. 37 Used to incorporate Filter into AsyncCompose. 38 39 To reduce the overhead of asynchronous context switching, 40 use_batch is set to True by default to process in batches 41 in apply_stream, regardless of the sync_filter's use_batch setting. 42 43 If performing CPU-bound and heavy processing, you can specify an executor 44 to offload the processing to the executor. However, due to Python's GIL 45 constraints, using ThreadPoolExecutor will not parallelize CPU-bound 46 processing, and the entire process will be locked. 47 48 By using ProcessPoolExecutor as the executor, it may be possible to 49 parallelize CPU-bound processing. However, for parallelizing CPU-bound 50 processing, it is recommended to use the hojichar.Parallel class to 51 parallelize synchronous Compose pipeline. 52 """ 53 super().__init__(*args, use_batch=use_batch, **kwargs) 54 self.sync_filter = sync_filter 55 self._has_external_executor = executor is not None 56 self._executor = executor or ThreadPoolExecutor() 57 self.batch_size = sync_filter.batch_size 58 59 async def apply(self, document: Document) -> Document: 60 loop = asyncio.get_running_loop() 61 return await loop.run_in_executor(self._executor, self.sync_filter.apply, document) 62 63 async def apply_batch(self, batch: Sequence[Document]) -> list[Document]: 64 loop = asyncio.get_running_loop() 65 return await loop.run_in_executor( 66 self._executor, 67 lambda: self.sync_filter.apply_batch(batch), 68 ) 69 70 async def shutdown(self) -> None: 71 self.sync_filter.shutdown() 72 if not self._has_external_executor: 73 self._executor.shutdown() 74 75 76class AsyncCompose(AsyncFilter): 77 def __init__( 78 self, 79 filters: list[AsyncFilter | Filter], 80 random_state: int | np.random.Generator | None = None, 81 executor: ThreadPoolExecutor | None = None, 82 *args: Any, 83 **kwargs: Any, 84 ): 85 super().__init__(random_state=random_state, *args, **kwargs) 86 self.logger = logging.getLogger(f"{self.__module__}.{self.__class__.__name__}") 87 self._statistics.name = "Total" 88 self._has_external_executor = executor is not None 89 self._executor = executor or ThreadPoolExecutor() 90 self.set_filters(filters) 91 92 def set_filters(self, filters: list[AsyncFilter | Filter]) -> None: 93 self.filters: list[AsyncFilter] = [] 94 filter_idx = 0 95 for f in filters: 96 if isinstance(f, (AsyncCompose, Compose)): 97 for sub in f.filters: 98 name = f"{filter_idx}-{sub.__class__.__name__}" 99 if isinstance(sub, Filter): 100 name = f"{filter_idx}-{sub.__class__.__name__}" 101 sub = AsyncFilterAdapter(sub, executor=self._executor) 102 103 sub._set_rng_if_not_initialized(self._rng) 104 sub.name = name 105 sub._statistics.name = name 106 self.filters.append(sub) 107 filter_idx += 1 108 else: 109 name = f"{filter_idx}-{f.__class__.__name__}" 110 if isinstance(f, Filter): 111 name = f"{filter_idx}-{f.__class__.__name__}" 112 f = AsyncFilterAdapter(f, executor=self._executor) 113 f._set_rng_if_not_initialized(self._rng) 114 f.name = name 115 f._statistics.name = name 116 self.filters.append(f) 117 filter_idx += 1 118 119 async def apply(self, document: Document) -> Document: 120 stat = get_doc_info(document) 121 for filter_idx, filt in enumerate(self.filters): 122 document = await filt._apply(document) 123 new_stat = get_doc_info(document) 124 async with self._stats_lock: 125 self._statistics.update_by_diff(stat, new_stat) 126 return document 127 128 async def apply_batch(self, batch: Sequence[Document]) -> list[Document]: 129 stats = [get_doc_info(doc) for doc in batch] 130 for i, filt in enumerate(self.filters): 131 batch = await filt._apply_batch(batch) 132 batch = await self._finalize_batch(batch, stats) 133 return list(batch) 134 135 async def apply_stream( 136 self, 137 stream: AsyncIterable[Document] | Iterable[Document], 138 ) -> AsyncGenerator[Document, None]: 139 async_stream = handle_stream_as_async(stream, chunk_size=1000, executor=self._executor) 140 async_stream = self._count_input_stats(async_stream) 141 142 for i, filt in enumerate(self.filters): 143 async_stream = filt.apply_stream(async_stream) 144 145 async for doc in async_stream: 146 in_stat = doc._get_initial_stats() 147 if in_stat is None: 148 in_stat = get_doc_info(doc) 149 self.logger.debug( 150 "Initial stats missing for document during async stream aggregation; " 151 "using current stats as fallback" 152 ) 153 out_stat = get_doc_info(doc) 154 async with self._stats_lock: 155 self._statistics.update_by_diff(in_stat, out_stat) 156 doc._clear_initial_stats() 157 yield doc 158 159 def imap_apply( 160 self, 161 stream: AsyncIterable[Document] | Iterable[Document], 162 *, 163 buffer_size: int = 128, 164 shutdown: bool = True, 165 ) -> AsyncToSyncIterator[Document]: 166 """Synchronously consume this asynchronous pipeline. 167 168 The returned iterator runs the pipeline on a dedicated background event loop. It is 169 intended for one-shot use and shuts down the pipeline after consumption by default. 170 Use the iterator as a context manager if iteration may stop early. 171 """ 172 173 finalizer = self.shutdown if shutdown else None 174 return handle_async_stream_as_sync( 175 self.apply_stream(stream), 176 buffer_size=buffer_size, 177 finalizer=finalizer, 178 ) 179 180 async def _count_input_stats( 181 self, async_stream: AsyncIterable[Document] 182 ) -> AsyncGenerator[Document, None]: 183 async for doc in async_stream: 184 doc._set_initial_stats(get_doc_info(doc)) 185 yield doc 186 187 def get_total_statistics(self) -> list[Statistics]: 188 """ 189 Get the statistics of the Compose object and sub filters. 190 191 The statistics of the Compose class are stored in an object with the name "Total", 192 and sub-filters's are stored with names in the format {filter_index}-{filter class name}. 193 """ 194 stats = [] 195 stats.append(self.get_statistics()) 196 for i, filt in enumerate(self.filters): 197 stats.append(filt.get_statistics()) 198 return stats 199 200 def get_total_statistics_map(self) -> list[dict[str, Any]]: 201 """ 202 Get the statistics of the Compose object and sub filters as a list of dictionaries. 203 """ 204 stats = self.get_total_statistics() 205 return [stat.to_dict() for stat in stats] 206 207 @property 208 def statistics(self) -> dict: 209 """ 210 Deprecated 211 212 Get the statistics of the Compose object and sub filters. 213 214 This property is retained for compatibility with previous versions. 215 Please use `get_total_statistics` or `get_total_statistics_map` instead. 216 """ 217 return inspection.statistics_obj_adapter( # type: ignore 218 self.get_total_statistics() 219 ).get_human_readable_values() 220 221 @property 222 def statistics_obj(self) -> inspection.StatsContainer: 223 """ 224 Deprecated 225 226 Get the statistics of the AsyncCompose object and sub filters. 227 This method returns a StatsContainer object which contains the statistics 228 of the AsyncCompose object and sub filters. 229 230 This property is retained for compatibility with previous versions. 231 Please use `get_total_statistics` or `get_total_statistics_map` instead. 232 """ 233 return inspection.statistics_obj_adapter(self.get_total_statistics()) # type: ignore 234 235 async def shutdown(self) -> None: 236 for filt in self.filters: 237 await filt.shutdown() 238 if not self._has_external_executor: 239 self._executor.shutdown() 240 241 async def __aenter__(self) -> "AsyncCompose": 242 return self 243 244 async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: 245 await self.shutdown() 246 if exc_type is not None: 247 raise exc_value
23class AsyncFilterAdapter(AsyncFilter): 24 """ 25 Adapter class for executing hojichar.Filter asynchronously. 26 """ 27 28 def __init__( 29 self, 30 sync_filter: Filter, 31 *args: Any, 32 executor: Executor | None = None, 33 use_batch: bool = True, 34 **kwargs: Any, 35 ): 36 """ 37 Adapter class for executing hojichar.Filter asynchronously. 38 Used to incorporate Filter into AsyncCompose. 39 40 To reduce the overhead of asynchronous context switching, 41 use_batch is set to True by default to process in batches 42 in apply_stream, regardless of the sync_filter's use_batch setting. 43 44 If performing CPU-bound and heavy processing, you can specify an executor 45 to offload the processing to the executor. However, due to Python's GIL 46 constraints, using ThreadPoolExecutor will not parallelize CPU-bound 47 processing, and the entire process will be locked. 48 49 By using ProcessPoolExecutor as the executor, it may be possible to 50 parallelize CPU-bound processing. However, for parallelizing CPU-bound 51 processing, it is recommended to use the hojichar.Parallel class to 52 parallelize synchronous Compose pipeline. 53 """ 54 super().__init__(*args, use_batch=use_batch, **kwargs) 55 self.sync_filter = sync_filter 56 self._has_external_executor = executor is not None 57 self._executor = executor or ThreadPoolExecutor() 58 self.batch_size = sync_filter.batch_size 59 60 async def apply(self, document: Document) -> Document: 61 loop = asyncio.get_running_loop() 62 return await loop.run_in_executor(self._executor, self.sync_filter.apply, document) 63 64 async def apply_batch(self, batch: Sequence[Document]) -> list[Document]: 65 loop = asyncio.get_running_loop() 66 return await loop.run_in_executor( 67 self._executor, 68 lambda: self.sync_filter.apply_batch(batch), 69 ) 70 71 async def shutdown(self) -> None: 72 self.sync_filter.shutdown() 73 if not self._has_external_executor: 74 self._executor.shutdown()
Adapter class for executing hojichar.Filter asynchronously.
28 def __init__( 29 self, 30 sync_filter: Filter, 31 *args: Any, 32 executor: Executor | None = None, 33 use_batch: bool = True, 34 **kwargs: Any, 35 ): 36 """ 37 Adapter class for executing hojichar.Filter asynchronously. 38 Used to incorporate Filter into AsyncCompose. 39 40 To reduce the overhead of asynchronous context switching, 41 use_batch is set to True by default to process in batches 42 in apply_stream, regardless of the sync_filter's use_batch setting. 43 44 If performing CPU-bound and heavy processing, you can specify an executor 45 to offload the processing to the executor. However, due to Python's GIL 46 constraints, using ThreadPoolExecutor will not parallelize CPU-bound 47 processing, and the entire process will be locked. 48 49 By using ProcessPoolExecutor as the executor, it may be possible to 50 parallelize CPU-bound processing. However, for parallelizing CPU-bound 51 processing, it is recommended to use the hojichar.Parallel class to 52 parallelize synchronous Compose pipeline. 53 """ 54 super().__init__(*args, use_batch=use_batch, **kwargs) 55 self.sync_filter = sync_filter 56 self._has_external_executor = executor is not None 57 self._executor = executor or ThreadPoolExecutor() 58 self.batch_size = sync_filter.batch_size
Adapter class for executing hojichar.Filter asynchronously. Used to incorporate Filter into AsyncCompose.
To reduce the overhead of asynchronous context switching, use_batch is set to True by default to process in batches in apply_stream, regardless of the sync_filter's use_batch setting.
If performing CPU-bound and heavy processing, you can specify an executor to offload the processing to the executor. However, due to Python's GIL constraints, using ThreadPoolExecutor will not parallelize CPU-bound processing, and the entire process will be locked.
By using ProcessPoolExecutor as the executor, it may be possible to parallelize CPU-bound processing. However, for parallelizing CPU-bound processing, it is recommended to use the hojichar.Parallel class to parallelize synchronous Compose pipeline.
60 async def apply(self, document: Document) -> Document: 61 loop = asyncio.get_running_loop() 62 return await loop.run_in_executor(self._executor, self.sync_filter.apply, document)
Definition of async filter behavior.
In this method, the filter will modify document.text or
document.extras and set document.is_rejected = True to discard the document.
Parameters
document : Document Input document
Returns
Document Processed Document
64 async def apply_batch(self, batch: Sequence[Document]) -> list[Document]: 65 loop = asyncio.get_running_loop() 66 return await loop.run_in_executor( 67 self._executor, 68 lambda: self.sync_filter.apply_batch(batch), 69 )
Apply the filter to a Sequence of documents.
By default, the processing implemented in apply is executed asynchronously and concurrently.
If the filter processing can be optimized for batch processing, override this method.
77class AsyncCompose(AsyncFilter): 78 def __init__( 79 self, 80 filters: list[AsyncFilter | Filter], 81 random_state: int | np.random.Generator | None = None, 82 executor: ThreadPoolExecutor | None = None, 83 *args: Any, 84 **kwargs: Any, 85 ): 86 super().__init__(random_state=random_state, *args, **kwargs) 87 self.logger = logging.getLogger(f"{self.__module__}.{self.__class__.__name__}") 88 self._statistics.name = "Total" 89 self._has_external_executor = executor is not None 90 self._executor = executor or ThreadPoolExecutor() 91 self.set_filters(filters) 92 93 def set_filters(self, filters: list[AsyncFilter | Filter]) -> None: 94 self.filters: list[AsyncFilter] = [] 95 filter_idx = 0 96 for f in filters: 97 if isinstance(f, (AsyncCompose, Compose)): 98 for sub in f.filters: 99 name = f"{filter_idx}-{sub.__class__.__name__}" 100 if isinstance(sub, Filter): 101 name = f"{filter_idx}-{sub.__class__.__name__}" 102 sub = AsyncFilterAdapter(sub, executor=self._executor) 103 104 sub._set_rng_if_not_initialized(self._rng) 105 sub.name = name 106 sub._statistics.name = name 107 self.filters.append(sub) 108 filter_idx += 1 109 else: 110 name = f"{filter_idx}-{f.__class__.__name__}" 111 if isinstance(f, Filter): 112 name = f"{filter_idx}-{f.__class__.__name__}" 113 f = AsyncFilterAdapter(f, executor=self._executor) 114 f._set_rng_if_not_initialized(self._rng) 115 f.name = name 116 f._statistics.name = name 117 self.filters.append(f) 118 filter_idx += 1 119 120 async def apply(self, document: Document) -> Document: 121 stat = get_doc_info(document) 122 for filter_idx, filt in enumerate(self.filters): 123 document = await filt._apply(document) 124 new_stat = get_doc_info(document) 125 async with self._stats_lock: 126 self._statistics.update_by_diff(stat, new_stat) 127 return document 128 129 async def apply_batch(self, batch: Sequence[Document]) -> list[Document]: 130 stats = [get_doc_info(doc) for doc in batch] 131 for i, filt in enumerate(self.filters): 132 batch = await filt._apply_batch(batch) 133 batch = await self._finalize_batch(batch, stats) 134 return list(batch) 135 136 async def apply_stream( 137 self, 138 stream: AsyncIterable[Document] | Iterable[Document], 139 ) -> AsyncGenerator[Document, None]: 140 async_stream = handle_stream_as_async(stream, chunk_size=1000, executor=self._executor) 141 async_stream = self._count_input_stats(async_stream) 142 143 for i, filt in enumerate(self.filters): 144 async_stream = filt.apply_stream(async_stream) 145 146 async for doc in async_stream: 147 in_stat = doc._get_initial_stats() 148 if in_stat is None: 149 in_stat = get_doc_info(doc) 150 self.logger.debug( 151 "Initial stats missing for document during async stream aggregation; " 152 "using current stats as fallback" 153 ) 154 out_stat = get_doc_info(doc) 155 async with self._stats_lock: 156 self._statistics.update_by_diff(in_stat, out_stat) 157 doc._clear_initial_stats() 158 yield doc 159 160 def imap_apply( 161 self, 162 stream: AsyncIterable[Document] | Iterable[Document], 163 *, 164 buffer_size: int = 128, 165 shutdown: bool = True, 166 ) -> AsyncToSyncIterator[Document]: 167 """Synchronously consume this asynchronous pipeline. 168 169 The returned iterator runs the pipeline on a dedicated background event loop. It is 170 intended for one-shot use and shuts down the pipeline after consumption by default. 171 Use the iterator as a context manager if iteration may stop early. 172 """ 173 174 finalizer = self.shutdown if shutdown else None 175 return handle_async_stream_as_sync( 176 self.apply_stream(stream), 177 buffer_size=buffer_size, 178 finalizer=finalizer, 179 ) 180 181 async def _count_input_stats( 182 self, async_stream: AsyncIterable[Document] 183 ) -> AsyncGenerator[Document, None]: 184 async for doc in async_stream: 185 doc._set_initial_stats(get_doc_info(doc)) 186 yield doc 187 188 def get_total_statistics(self) -> list[Statistics]: 189 """ 190 Get the statistics of the Compose object and sub filters. 191 192 The statistics of the Compose class are stored in an object with the name "Total", 193 and sub-filters's are stored with names in the format {filter_index}-{filter class name}. 194 """ 195 stats = [] 196 stats.append(self.get_statistics()) 197 for i, filt in enumerate(self.filters): 198 stats.append(filt.get_statistics()) 199 return stats 200 201 def get_total_statistics_map(self) -> list[dict[str, Any]]: 202 """ 203 Get the statistics of the Compose object and sub filters as a list of dictionaries. 204 """ 205 stats = self.get_total_statistics() 206 return [stat.to_dict() for stat in stats] 207 208 @property 209 def statistics(self) -> dict: 210 """ 211 Deprecated 212 213 Get the statistics of the Compose object and sub filters. 214 215 This property is retained for compatibility with previous versions. 216 Please use `get_total_statistics` or `get_total_statistics_map` instead. 217 """ 218 return inspection.statistics_obj_adapter( # type: ignore 219 self.get_total_statistics() 220 ).get_human_readable_values() 221 222 @property 223 def statistics_obj(self) -> inspection.StatsContainer: 224 """ 225 Deprecated 226 227 Get the statistics of the AsyncCompose object and sub filters. 228 This method returns a StatsContainer object which contains the statistics 229 of the AsyncCompose object and sub filters. 230 231 This property is retained for compatibility with previous versions. 232 Please use `get_total_statistics` or `get_total_statistics_map` instead. 233 """ 234 return inspection.statistics_obj_adapter(self.get_total_statistics()) # type: ignore 235 236 async def shutdown(self) -> None: 237 for filt in self.filters: 238 await filt.shutdown() 239 if not self._has_external_executor: 240 self._executor.shutdown() 241 242 async def __aenter__(self) -> "AsyncCompose": 243 return self 244 245 async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: 246 await self.shutdown() 247 if exc_type is not None: 248 raise exc_value
Helper class that provides a standard way to create an ABC using inheritance.
78 def __init__( 79 self, 80 filters: list[AsyncFilter | Filter], 81 random_state: int | np.random.Generator | None = None, 82 executor: ThreadPoolExecutor | None = None, 83 *args: Any, 84 **kwargs: Any, 85 ): 86 super().__init__(random_state=random_state, *args, **kwargs) 87 self.logger = logging.getLogger(f"{self.__module__}.{self.__class__.__name__}") 88 self._statistics.name = "Total" 89 self._has_external_executor = executor is not None 90 self._executor = executor or ThreadPoolExecutor() 91 self.set_filters(filters)
Base class for asynchronous filters.
Parameters
p : float
The probability of applying the filter.
If p is 1, the filter will always be applied.
skip_rejected : bool
If True, the filter will skip documents that are already rejected.
If you want to apply the filter to all documents (e.g., postprocess), set this to False.
random_state : Optional[Union[int, np.random.Generator]]
Seed for the random number generator.
If None is specified, the random number generator managed by the Compose class will be used.
use_batch : bool
If True, the filter will process documents in batches in the apply_stream method.
batch_size : int
The size of the batch to process documents in the apply_stream method.
When apply_batch is not overridden, this is also the maximum number of
in-flight document tasks used by the sliding-window scheduler. Ordered
processing applies backpressure after at most two windows of started but
not yet yielded documents.
ordered : bool
If True, apply_stream yields documents in input order. If False, filters
using the default apply_batch implementation yield documents as soon as their
processing completes.
93 def set_filters(self, filters: list[AsyncFilter | Filter]) -> None: 94 self.filters: list[AsyncFilter] = [] 95 filter_idx = 0 96 for f in filters: 97 if isinstance(f, (AsyncCompose, Compose)): 98 for sub in f.filters: 99 name = f"{filter_idx}-{sub.__class__.__name__}" 100 if isinstance(sub, Filter): 101 name = f"{filter_idx}-{sub.__class__.__name__}" 102 sub = AsyncFilterAdapter(sub, executor=self._executor) 103 104 sub._set_rng_if_not_initialized(self._rng) 105 sub.name = name 106 sub._statistics.name = name 107 self.filters.append(sub) 108 filter_idx += 1 109 else: 110 name = f"{filter_idx}-{f.__class__.__name__}" 111 if isinstance(f, Filter): 112 name = f"{filter_idx}-{f.__class__.__name__}" 113 f = AsyncFilterAdapter(f, executor=self._executor) 114 f._set_rng_if_not_initialized(self._rng) 115 f.name = name 116 f._statistics.name = name 117 self.filters.append(f) 118 filter_idx += 1
120 async def apply(self, document: Document) -> Document: 121 stat = get_doc_info(document) 122 for filter_idx, filt in enumerate(self.filters): 123 document = await filt._apply(document) 124 new_stat = get_doc_info(document) 125 async with self._stats_lock: 126 self._statistics.update_by_diff(stat, new_stat) 127 return document
Definition of async filter behavior.
In this method, the filter will modify document.text or
document.extras and set document.is_rejected = True to discard the document.
Parameters
document : Document Input document
Returns
Document Processed Document
129 async def apply_batch(self, batch: Sequence[Document]) -> list[Document]: 130 stats = [get_doc_info(doc) for doc in batch] 131 for i, filt in enumerate(self.filters): 132 batch = await filt._apply_batch(batch) 133 batch = await self._finalize_batch(batch, stats) 134 return list(batch)
Apply the filter to a Sequence of documents.
By default, the processing implemented in apply is executed asynchronously and concurrently.
If the filter processing can be optimized for batch processing, override this method.
136 async def apply_stream( 137 self, 138 stream: AsyncIterable[Document] | Iterable[Document], 139 ) -> AsyncGenerator[Document, None]: 140 async_stream = handle_stream_as_async(stream, chunk_size=1000, executor=self._executor) 141 async_stream = self._count_input_stats(async_stream) 142 143 for i, filt in enumerate(self.filters): 144 async_stream = filt.apply_stream(async_stream) 145 146 async for doc in async_stream: 147 in_stat = doc._get_initial_stats() 148 if in_stat is None: 149 in_stat = get_doc_info(doc) 150 self.logger.debug( 151 "Initial stats missing for document during async stream aggregation; " 152 "using current stats as fallback" 153 ) 154 out_stat = get_doc_info(doc) 155 async with self._stats_lock: 156 self._statistics.update_by_diff(in_stat, out_stat) 157 doc._clear_initial_stats() 158 yield doc
Apply the filter to a stream of documents (Iterable or AsyncIterable).
If use_batch is set to True at initialization, the filter will process documents in batches.
If the stream is not asynchronous, use handle_stream_as_async to convert it to an asynchronous stream.
Even if an exception occurs during processing, the process will continue, and the following actions will be taken:
- Set the
is_rejectedflag of the document toTrue - Set the error details in
reject_reason - Increment the
errorscount in the statistics retrievable viaget_statistics
160 def imap_apply( 161 self, 162 stream: AsyncIterable[Document] | Iterable[Document], 163 *, 164 buffer_size: int = 128, 165 shutdown: bool = True, 166 ) -> AsyncToSyncIterator[Document]: 167 """Synchronously consume this asynchronous pipeline. 168 169 The returned iterator runs the pipeline on a dedicated background event loop. It is 170 intended for one-shot use and shuts down the pipeline after consumption by default. 171 Use the iterator as a context manager if iteration may stop early. 172 """ 173 174 finalizer = self.shutdown if shutdown else None 175 return handle_async_stream_as_sync( 176 self.apply_stream(stream), 177 buffer_size=buffer_size, 178 finalizer=finalizer, 179 )
Synchronously consume this asynchronous pipeline.
The returned iterator runs the pipeline on a dedicated background event loop. It is intended for one-shot use and shuts down the pipeline after consumption by default. Use the iterator as a context manager if iteration may stop early.
188 def get_total_statistics(self) -> list[Statistics]: 189 """ 190 Get the statistics of the Compose object and sub filters. 191 192 The statistics of the Compose class are stored in an object with the name "Total", 193 and sub-filters's are stored with names in the format {filter_index}-{filter class name}. 194 """ 195 stats = [] 196 stats.append(self.get_statistics()) 197 for i, filt in enumerate(self.filters): 198 stats.append(filt.get_statistics()) 199 return stats
Get the statistics of the Compose object and sub filters.
The statistics of the Compose class are stored in an object with the name "Total", and sub-filters's are stored with names in the format {filter_index}-{filter class name}.
201 def get_total_statistics_map(self) -> list[dict[str, Any]]: 202 """ 203 Get the statistics of the Compose object and sub filters as a list of dictionaries. 204 """ 205 stats = self.get_total_statistics() 206 return [stat.to_dict() for stat in stats]
Get the statistics of the Compose object and sub filters as a list of dictionaries.
Deprecated
Get the statistics of the Compose object and sub filters.
This property is retained for compatibility with previous versions.
Please use get_total_statistics or get_total_statistics_map instead.
Deprecated
Get the statistics of the AsyncCompose object and sub filters. This method returns a StatsContainer object which contains the statistics of the AsyncCompose object and sub filters.
This property is retained for compatibility with previous versions.
Please use get_total_statistics or get_total_statistics_map instead.