|
4 | 4 | from collections import Counter |
5 | 5 | from copy import copy |
6 | 6 | from functools import lru_cache |
7 | | -from typing import Any, Callable, Dict, List, Optional, Union |
| 7 | +from typing import Any, Callable, Dict, List, Optional, Tuple, Union |
8 | 8 |
|
9 | 9 | from packaging.specifiers import SpecifierSet |
10 | 10 | from packaging.version import Version |
|
50 | 50 | WORKFLOWS_CORE_PLUGIN_NAME = "workflows_core" |
51 | 51 |
|
52 | 52 |
|
| 53 | +def _get_env_configuration_state() -> Tuple[Tuple[str, ...], bool]: |
| 54 | + """ |
| 55 | + Returns current environment configuration state for cache keying. |
| 56 | + This ensures caches are invalidated when plugins or enterprise blocks change. |
| 57 | + """ |
| 58 | + plugins = tuple(get_plugin_modules()) |
| 59 | + return (plugins, LOAD_ENTERPRISE_BLOCKS) |
| 60 | + |
| 61 | + |
| 62 | +def clear_caches() -> None: |
| 63 | + """ |
| 64 | + Clear all LRU caches in this module. |
| 65 | + Useful for testing or when environment configuration changes. |
| 66 | + """ |
| 67 | + _cached_describe_available_blocks.cache_clear() |
| 68 | + load_core_workflow_blocks.cache_clear() |
| 69 | + _cached_load_all_defined_kinds.cache_clear() |
| 70 | + _cached_model_json_schema.cache_clear() |
| 71 | + _cached_describe_outputs.cache_clear() |
| 72 | + |
| 73 | + |
53 | 74 | def describe_available_blocks( |
54 | 75 | dynamic_blocks: List[BlockSpecification], |
55 | 76 | execution_engine_version: Optional[Union[str, Version]] = None, |
56 | 77 | ) -> BlocksDescription: |
| 78 | + # Fast path: cache for common case with no dynamic blocks |
| 79 | + if not dynamic_blocks: |
| 80 | + env_state = _get_env_configuration_state() |
| 81 | + return _cached_describe_available_blocks( |
| 82 | + execution_engine_version=execution_engine_version, |
| 83 | + env_state=env_state, |
| 84 | + ) |
| 85 | + |
57 | 86 | blocks = ( |
58 | 87 | load_workflow_blocks(execution_engine_version=execution_engine_version) |
59 | 88 | + dynamic_blocks |
@@ -87,7 +116,54 @@ def describe_available_blocks( |
87 | 116 | ) |
88 | 117 | ) |
89 | 118 | _validate_loaded_blocks_manifest_type_identifiers(blocks=result) |
90 | | - declared_kinds = load_all_defined_kinds() |
| 119 | + declared_kinds = _cached_load_all_defined_kinds( |
| 120 | + env_state=_get_env_configuration_state() |
| 121 | + ) |
| 122 | + return BlocksDescription(blocks=result, declared_kinds=declared_kinds) |
| 123 | + |
| 124 | + |
| 125 | +@lru_cache(maxsize=8) |
| 126 | +def _cached_describe_available_blocks( |
| 127 | + execution_engine_version: Optional[Union[str, Version]] = None, |
| 128 | + env_state: Tuple[Tuple[str, ...], bool] = None, |
| 129 | +) -> BlocksDescription: |
| 130 | + """Cached version for when there are no dynamic blocks (common case). |
| 131 | +
|
| 132 | + Args: |
| 133 | + execution_engine_version: Version filter for blocks |
| 134 | + env_state: Tuple of (plugins, enterprise_blocks_flag) for cache invalidation |
| 135 | + """ |
| 136 | + blocks = load_workflow_blocks(execution_engine_version=execution_engine_version) |
| 137 | + result = [] |
| 138 | + for block in blocks: |
| 139 | + block_schema = _cached_model_json_schema(block.manifest_class) |
| 140 | + outputs_manifest = _cached_describe_outputs(block.manifest_class) |
| 141 | + manifest_type_identifiers = get_manifest_type_identifiers( |
| 142 | + block_schema=block_schema, |
| 143 | + block_source=block.block_source, |
| 144 | + block_identifier=block.identifier, |
| 145 | + ) |
| 146 | + result.append( |
| 147 | + BlockDescription( |
| 148 | + manifest_class=block.manifest_class, |
| 149 | + block_class=block.block_class, |
| 150 | + block_schema=block_schema, |
| 151 | + outputs_manifest=outputs_manifest, |
| 152 | + block_source=block.block_source, |
| 153 | + fully_qualified_block_class_name=block.identifier, |
| 154 | + human_friendly_block_name=build_human_friendly_block_name( |
| 155 | + fully_qualified_name=block.identifier, block_schema=block_schema |
| 156 | + ), |
| 157 | + manifest_type_identifier=manifest_type_identifiers[0], |
| 158 | + manifest_type_identifier_aliases=manifest_type_identifiers[1:], |
| 159 | + execution_engine_compatibility=block.manifest_class.get_execution_engine_compatibility(), |
| 160 | + input_dimensionality_offsets=block.manifest_class.get_input_dimensionality_offsets(), |
| 161 | + dimensionality_reference_property=block.manifest_class.get_dimensionality_reference_property(), |
| 162 | + output_dimensionality_offset=block.manifest_class.get_output_dimensionality_offset(), |
| 163 | + ) |
| 164 | + ) |
| 165 | + _validate_loaded_blocks_manifest_type_identifiers(blocks=result) |
| 166 | + declared_kinds = _cached_load_all_defined_kinds(env_state=env_state) |
91 | 167 | return BlocksDescription(blocks=result, declared_kinds=declared_kinds) |
92 | 168 |
|
93 | 169 |
|
@@ -350,6 +426,18 @@ def _validate_used_kinds_uniqueness(declared_kinds: List[Kind]) -> None: |
350 | 426 | ) |
351 | 427 |
|
352 | 428 |
|
| 429 | +@lru_cache(maxsize=8) |
| 430 | +def _cached_load_all_defined_kinds( |
| 431 | + env_state: Tuple[Tuple[str, ...], bool] = None, |
| 432 | +) -> List[Kind]: |
| 433 | + """Cached version of load_all_defined_kinds. |
| 434 | +
|
| 435 | + Args: |
| 436 | + env_state: Tuple of (plugins, enterprise_blocks_flag) for cache invalidation |
| 437 | + """ |
| 438 | + return load_all_defined_kinds() |
| 439 | + |
| 440 | + |
353 | 441 | def load_all_defined_kinds() -> List[Kind]: |
354 | 442 | core_blocks_kinds = load_kinds() |
355 | 443 | plugins_kinds = load_plugins_kinds() |
|
0 commit comments