Manages the registration and execution of plugins. The PluginManager is an internal class that orchestrates the invocation of plugin callbacks at key points in the SDK's execution lifecycle. It maintains a list of registered plugins and ensures they are called in the order they were registe
| 58 | |
| 59 | |
| 60 | class PluginManager: |
| 61 | """Manages the registration and execution of plugins. |
| 62 | |
| 63 | The PluginManager is an internal class that orchestrates the invocation of |
| 64 | plugin callbacks at key points in the SDK's execution lifecycle. It maintains |
| 65 | a list of registered plugins and ensures they are called in the order they |
| 66 | were registered. |
| 67 | |
| 68 | The core execution logic implements an "early exit" strategy: if any plugin |
| 69 | callback returns a non-`None` value, the execution of subsequent plugins for |
| 70 | that specific event is halted, and the returned value is propagated up the |
| 71 | call stack. This allows plugins to short-circuit operations like agent runs, |
| 72 | tool calls, or model requests. |
| 73 | """ |
| 74 | |
| 75 | def __init__( |
| 76 | self, |
| 77 | plugins: Optional[List[BasePlugin]] = None, |
| 78 | close_timeout: float = 5.0, |
| 79 | ): |
| 80 | """Initializes the plugin service. |
| 81 | |
| 82 | Args: |
| 83 | plugins: An optional list of plugins to register upon initialization. |
| 84 | close_timeout: The timeout in seconds for each plugin's close method. |
| 85 | """ |
| 86 | self.plugins: List[BasePlugin] = [] |
| 87 | self._close_timeout = close_timeout |
| 88 | self._skip_closing_plugins = False |
| 89 | if plugins: |
| 90 | for plugin in plugins: |
| 91 | self.register_plugin(plugin) |
| 92 | |
| 93 | def set_skip_closing_plugins(self, value: bool) -> None: |
| 94 | """Controls whether `close()` will tear down the registered plugins. |
| 95 | |
| 96 | Set to True when the plugins are owned by another component (e.g. a parent |
| 97 | `Runner` whose plugin list this manager is sharing). When set, subsequent |
| 98 | calls to `close()` become a no-op so the shared plugins are not torn down |
| 99 | while still in use. |
| 100 | |
| 101 | Args: |
| 102 | value: True to skip closing the plugins; False (default) to close them |
| 103 | normally. |
| 104 | """ |
| 105 | self._skip_closing_plugins = value |
| 106 | |
| 107 | def register_plugin(self, plugin: BasePlugin) -> None: |
| 108 | """Registers a new plugin. |
| 109 | |
| 110 | Args: |
| 111 | plugin: The plugin instance to register. |
| 112 | |
| 113 | Raises: |
| 114 | ValueError: If a plugin with the same name is already registered. |
| 115 | """ |
| 116 | if any(p.name == plugin.name for p in self.plugins): |
| 117 | raise ValueError(f"Plugin with name '{plugin.name}' already registered.") |
no outgoing calls