Class: SwarmSDK::Swarm
- Inherits:
-
Object
- Object
- SwarmSDK::Swarm
- Includes:
- Concerns::Cleanupable, Concerns::Snapshotable, Concerns::Validatable, HookTriggers, LoggingCallbacks
- Defined in:
- lib/swarm_sdk/swarm.rb,
lib/swarm_sdk/swarm/builder.rb,
lib/swarm_sdk/swarm/executor.rb,
lib/swarm_sdk/swarm/hook_triggers.rb,
lib/swarm_sdk/swarm/mcp_configurator.rb,
lib/swarm_sdk/swarm/agent_initializer.rb,
lib/swarm_sdk/swarm/logging_callbacks.rb,
lib/swarm_sdk/swarm/tool_configurator.rb,
lib/swarm_sdk/swarm/all_agents_builder.rb,
lib/swarm_sdk/swarm/swarm_registry_builder.rb
Overview
Swarm orchestrates multiple AI agents with shared rate limiting and coordination.
This is the main user-facing API for SwarmSDK. Users create swarms using:
- Ruby DSL: SwarmSDK.build { ... } (Recommended)
- YAML String: SwarmSDK.load(yaml, base_dir:)
- YAML File: SwarmSDK.load_file(path)
- Direct API: Swarm.new + add_agent (Advanced)
Ruby DSL (Recommended)
swarm = SwarmSDK.build do
name "Development Team"
lead :backend
agent :backend do
model "gpt-5"
description "Backend developer"
prompt "You build APIs"
tools :Read, :Edit, :Bash
end
end
result = swarm.execute("Build authentication")
YAML String API
yaml = File.read("swarm.yml")
swarm = SwarmSDK.load(yaml, base_dir: "/path/to/project")
result = swarm.execute("Build authentication")
YAML File API (Convenience)
swarm = SwarmSDK.load_file("swarm.yml")
result = swarm.execute("Build authentication")
Direct API (Advanced)
swarm = Swarm.new(name: "Development Team")
backend_agent = Agent::Definition.new(:backend, {
description: "Backend developer",
model: "gpt-5",
system_prompt: "You build APIs and databases...",
tools: [:Read, :Edit, :Bash],
delegates_to: [:database]
})
swarm.add_agent(backend_agent)
swarm.lead = :backend
result = swarm.execute("Build authentication")
Architecture
All APIs converge on Agent::Definition for validation. Swarm delegates to specialized concerns:
- Agent::Definition: Validates configuration, builds system prompts
- AgentInitializer: Complex 5-pass agent setup
- ToolConfigurator: Tool creation and permissions (via AgentInitializer)
- McpConfigurator: MCP client management (via AgentInitializer)
Defined Under Namespace
Modules: HookTriggers, LoggingCallbacks Classes: AgentInitializer, AllAgentsBuilder, Builder, Executor, McpConfigurator, SwarmRegistryBuilder, ToolConfigurator
Constant Summary collapse
- DEFAULT_TOOLS =
Default tools available to all agents
ToolConfigurator::DEFAULT_TOOLS
Class Attribute Summary collapse
-
.mcp_log_level ⇒ Object
Returns the value of attribute mcp_log_level.
Instance Attribute Summary collapse
-
#agent_definitions ⇒ Object
readonly
Returns the value of attribute agent_definitions.
-
#agents ⇒ Object
readonly
Returns the value of attribute agents.
-
#allow_filesystem_tools ⇒ Object
readonly
Returns the value of attribute allow_filesystem_tools.
-
#config_for_hooks ⇒ Object
Returns the value of attribute config_for_hooks.
-
#delegation_call_stack ⇒ Object
Returns the value of attribute delegation_call_stack.
-
#delegation_instances ⇒ Object
readonly
Returns the value of attribute delegation_instances.
-
#first_message_sent ⇒ void
writeonly
Set first message sent flag (used by snapshot/restore).
-
#global_semaphore ⇒ Object
readonly
Returns the value of attribute global_semaphore.
-
#hook_registry ⇒ Object
readonly
Returns the value of attribute hook_registry.
-
#lead_agent ⇒ Object
readonly
Returns the value of attribute lead_agent.
-
#mcp_clients ⇒ Object
readonly
Returns the value of attribute mcp_clients.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#observer_configs ⇒ Object
readonly
Returns the value of attribute observer_configs.
-
#parent_swarm_id ⇒ Object
readonly
Returns the value of attribute parent_swarm_id.
-
#plugin_storages ⇒ Object
readonly
Returns the value of attribute plugin_storages.
-
#scratchpad_storage ⇒ Object
readonly
Returns the value of attribute scratchpad_storage.
-
#swarm_id ⇒ Object
readonly
Returns the value of attribute swarm_id.
-
#swarm_registry ⇒ Object
Returns the value of attribute swarm_registry.
Class Method Summary collapse
-
.apply_mcp_logging_configuration ⇒ void
Apply MCP logging configuration to RubyLLM::MCP.
-
.configure_mcp_logging(level = nil) ⇒ void
Configure MCP client logging globally.
Instance Method Summary collapse
-
#add_agent(definition) ⇒ self
Add an agent to the swarm.
-
#add_observer_config(config) ⇒ void
Add observer configuration.
-
#agent(name) ⇒ AgentChat
Get an agent chat instance by name.
-
#agent_definition(name) ⇒ AgentDefinition
Get an agent definition by name.
-
#agent_names ⇒ Array<Symbol>
Get all agent names.
-
#cleanup_observers ⇒ void
Cleanup observer subscriptions.
-
#context_breakdown ⇒ Hash{Symbol => Hash}
Get context usage breakdown for all agents.
- #delegation_instances_hash ⇒ Object
-
#execute(prompt, wait: true) {|Hash| ... } ⇒ Result, Async::Task
Execute a task using the lead agent.
-
#first_message_sent? ⇒ Boolean
Check if first message has been sent (for system reminder injection).
-
#initialize(name:, swarm_id: nil, parent_swarm_id: nil, global_concurrency: nil, default_local_concurrency: nil, scratchpad: nil, scratchpad_mode: :enabled, allow_filesystem_tools: nil) ⇒ Swarm
constructor
Initialize a new Swarm.
-
#lead=(name) ⇒ self
Set the lead agent (entry point for swarm execution).
-
#override_swarm_ids(swarm_id:, parent_swarm_id:) ⇒ void
Override swarm IDs for composable swarms.
-
#primary_agents ⇒ Object
Implement Snapshotable interface.
-
#register_hook(name, &block) ⇒ self
Register a named hook that can be referenced in agent configurations.
-
#reset_context! ⇒ void
Reset context for all agents.
-
#restore(snapshot, preserve_system_prompts: false) ⇒ RestoreResult
Restore conversation state from snapshot.
-
#scratchpad_enabled? ⇒ Boolean
Check if scratchpad tools are enabled.
-
#snapshot ⇒ Snapshot
Create snapshot of current conversation state.
-
#wait_for_observers ⇒ void
Wait for all observer tasks to complete.
Methods included from HookTriggers
#add_default_callback, #trigger_swarm_stop, #trigger_swarm_stop_final
Methods included from LoggingCallbacks
#emit_agent_start_events, #emit_agent_start_for, #emit_retroactive_agent_start_events, #register_default_logging_callbacks, #setup_logging, #setup_logging_for_all_agents
Methods included from Concerns::Cleanupable
Methods included from Concerns::Validatable
#emit_validation_warnings, #validate
Constructor Details
#initialize(name:, swarm_id: nil, parent_swarm_id: nil, global_concurrency: nil, default_local_concurrency: nil, scratchpad: nil, scratchpad_mode: :enabled, allow_filesystem_tools: nil) ⇒ Swarm
Initialize a new Swarm
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/swarm_sdk/swarm.rb', line 142 def initialize(name:, swarm_id: nil, parent_swarm_id: nil, global_concurrency: nil, default_local_concurrency: nil, scratchpad: nil, scratchpad_mode: :enabled, allow_filesystem_tools: nil) @name = name @swarm_id = swarm_id || generate_swarm_id(name) @parent_swarm_id = parent_swarm_id @global_concurrency = global_concurrency || SwarmSDK.config.global_concurrency_limit @default_local_concurrency = default_local_concurrency || SwarmSDK.config.local_concurrency_limit # Handle scratchpad_mode parameter # For Swarm: :enabled or :disabled (not :per_node - that's for nodes) @scratchpad_mode = validate_swarm_scratchpad_mode(scratchpad_mode) # Resolve allow_filesystem_tools with priority: # 1. Explicit parameter (if not nil) # 2. Global config @allow_filesystem_tools = if allow_filesystem_tools.nil? SwarmSDK.config.allow_filesystem_tools else allow_filesystem_tools end # Swarm registry for managing sub-swarms (initialized later if needed) @swarm_registry = nil # Delegation call stack for circular dependency detection @delegation_call_stack = [] # Shared semaphore for all agents @global_semaphore = Async::Semaphore.new(@global_concurrency) # Shared scratchpad storage for all agents (volatile) # Use provided scratchpad storage (for testing) or create volatile one based on mode @scratchpad_storage = if scratchpad scratchpad # Testing/internal use - explicit instance provided elsif @scratchpad_mode == :enabled Tools::Stores::ScratchpadStorage.new end # Per-agent plugin storages (persistent) # Format: { plugin_name => { agent_name => storage } } # Will be populated when agents are initialized @plugin_storages = {} # Hook registry for named hooks and swarm defaults @hook_registry = Hooks::Registry.new # Register default logging hooks register_default_logging_callbacks # Agent definitions and instances @agent_definitions = {} @agents = {} @delegation_instances = {} # { "delegate@delegator" => Agent::Chat } @agents_initialized = false @agent_contexts = {} # MCP clients per agent (for cleanup) @mcp_clients = Hash.new { |h, k| h[k] = [] } @lead_agent = nil # Track if first message has been sent @first_message_sent = false # Track if agent_start events have been emitted # This prevents duplicate emissions and ensures events are emitted when logging is ready @agent_start_events_emitted = false # Observer agent configurations @observer_configs = [] @observer_manager = nil end |
Class Attribute Details
.mcp_log_level ⇒ Object
Returns the value of attribute mcp_log_level.
104 105 106 |
# File 'lib/swarm_sdk/swarm.rb', line 104 def mcp_log_level @mcp_log_level end |
Instance Attribute Details
#agent_definitions ⇒ Object (readonly)
Returns the value of attribute agent_definitions.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def agent_definitions @agent_definitions end |
#agents ⇒ Object (readonly)
Returns the value of attribute agents.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def agents @agents end |
#allow_filesystem_tools ⇒ Object (readonly)
Returns the value of attribute allow_filesystem_tools.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def allow_filesystem_tools @allow_filesystem_tools end |
#config_for_hooks ⇒ Object
Returns the value of attribute config_for_hooks.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def config_for_hooks @config_for_hooks end |
#delegation_call_stack ⇒ Object
Returns the value of attribute delegation_call_stack.
76 77 78 |
# File 'lib/swarm_sdk/swarm.rb', line 76 def delegation_call_stack @delegation_call_stack end |
#delegation_instances ⇒ Object (readonly)
Returns the value of attribute delegation_instances.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def delegation_instances @delegation_instances end |
#first_message_sent=(value) ⇒ void (writeonly)
This method returns an undefined value.
Set first message sent flag (used by snapshot/restore)
97 98 99 |
# File 'lib/swarm_sdk/swarm.rb', line 97 def (value) @first_message_sent = value end |
#global_semaphore ⇒ Object (readonly)
Returns the value of attribute global_semaphore.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def global_semaphore @global_semaphore end |
#hook_registry ⇒ Object (readonly)
Returns the value of attribute hook_registry.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def hook_registry @hook_registry end |
#lead_agent ⇒ Object (readonly)
Returns the value of attribute lead_agent.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def lead_agent @lead_agent end |
#mcp_clients ⇒ Object (readonly)
Returns the value of attribute mcp_clients.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def mcp_clients @mcp_clients end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def name @name end |
#observer_configs ⇒ Object (readonly)
Returns the value of attribute observer_configs.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def observer_configs @observer_configs end |
#parent_swarm_id ⇒ Object (readonly)
Returns the value of attribute parent_swarm_id.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def parent_swarm_id @parent_swarm_id end |
#plugin_storages ⇒ Object (readonly)
Returns the value of attribute plugin_storages.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def plugin_storages @plugin_storages end |
#scratchpad_storage ⇒ Object (readonly)
Returns the value of attribute scratchpad_storage.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def scratchpad_storage @scratchpad_storage end |
#swarm_id ⇒ Object (readonly)
Returns the value of attribute swarm_id.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def swarm_id @swarm_id end |
#swarm_registry ⇒ Object
Returns the value of attribute swarm_registry.
75 76 77 |
# File 'lib/swarm_sdk/swarm.rb', line 75 def swarm_registry @swarm_registry end |
Class Method Details
.apply_mcp_logging_configuration ⇒ void
This method returns an undefined value.
Apply MCP logging configuration to RubyLLM::MCP
121 122 123 124 125 126 127 128 129 |
# File 'lib/swarm_sdk/swarm.rb', line 121 def apply_mcp_logging_configuration return if @mcp_logging_configured RubyLLM::MCP.configure do |config| config.log_level = @mcp_log_level || SwarmSDK.config.mcp_log_level end @mcp_logging_configured = true end |
.configure_mcp_logging(level = nil) ⇒ void
This method returns an undefined value.
Configure MCP client logging globally
This should be called before creating any swarms that use MCP servers. The configuration is global and affects all MCP clients.
113 114 115 116 |
# File 'lib/swarm_sdk/swarm.rb', line 113 def configure_mcp_logging(level = nil) @mcp_log_level = level || SwarmSDK.config.mcp_log_level apply_mcp_logging_configuration end |
Instance Method Details
#add_agent(definition) ⇒ self
Add an agent to the swarm
Accepts only Agent::Definition objects. This ensures all validation happens in a single place (Agent::Definition) and keeps the API clean.
If the definition doesn't specify max_concurrent_tools, the swarm's default_local_concurrency is applied.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/swarm_sdk/swarm.rb', line 232 def add_agent(definition) unless definition.is_a?(Agent::Definition) raise ArgumentError, "Expected Agent::Definition, got #{definition.class}" end name = definition.name raise ConfigurationError, "Agent '#{name}' already exists" if @agent_definitions.key?(name) # Apply swarm's default_local_concurrency if max_concurrent_tools not set definition.max_concurrent_tools = @default_local_concurrency if definition.max_concurrent_tools.nil? @agent_definitions[name] = definition self end |
#add_observer_config(config) ⇒ void
This method returns an undefined value.
Add observer configuration
Called by Swarm::Builder to register observer agent configurations. Validates that the referenced agent exists.
459 460 461 462 |
# File 'lib/swarm_sdk/swarm.rb', line 459 def add_observer_config(config) validate_observer_agent(config.agent_name) @observer_configs << config end |
#agent(name) ⇒ AgentChat
Get an agent chat instance by name
342 343 344 345 346 347 |
# File 'lib/swarm_sdk/swarm.rb', line 342 def agent(name) name = name.to_sym initialize_agents unless @agents_initialized @agents[name] || raise(AgentNotFoundError, "Agent '#{name}' not found") end |
#agent_definition(name) ⇒ AgentDefinition
Get an agent definition by name
Use this to access and modify agent configuration:
swarm.agent_definition(:backend). = true
356 357 358 359 360 |
# File 'lib/swarm_sdk/swarm.rb', line 356 def agent_definition(name) name = name.to_sym @agent_definitions[name] || raise(AgentNotFoundError, "Agent '#{name}' not found") end |
#agent_names ⇒ Array<Symbol>
Get all agent names
365 366 367 |
# File 'lib/swarm_sdk/swarm.rb', line 365 def agent_names @agent_definitions.keys end |
#cleanup_observers ⇒ void
This method returns an undefined value.
Cleanup observer subscriptions
Called by Executor.cleanup_after_execution to unsubscribe observers. Matches the MCP cleanup pattern.
480 481 482 483 |
# File 'lib/swarm_sdk/swarm.rb', line 480 def cleanup_observers @observer_manager&.cleanup @observer_manager = nil end |
#context_breakdown ⇒ Hash{Symbol => Hash}
Get context usage breakdown for all agents
Returns per-agent context statistics including tokens used, context limit, usage percentage, and cost. Useful for monitoring context window consumption across the swarm.
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'lib/swarm_sdk/swarm.rb', line 392 def context_breakdown initialize_agents unless @agents_initialized breakdown = {} # Include primary agents @agents.each do |name, chat| breakdown[name] = build_agent_context_info(chat) end # Include delegation instances @delegation_instances.each do |instance_name, chat| breakdown[instance_name.to_sym] = build_agent_context_info(chat) end breakdown end |
#delegation_instances_hash ⇒ Object
415 416 417 |
# File 'lib/swarm_sdk/swarm.rb', line 415 def delegation_instances_hash @delegation_instances end |
#execute(prompt, wait: true) {|Hash| ... } ⇒ Result, Async::Task
Execute a task using the lead agent
The lead agent can delegate to other agents via tool calls, and the entire swarm coordinates with shared rate limiting. Supports reprompting via swarm_stop hooks.
By default, this method blocks until execution completes. Set wait: false to return an Async::Task immediately, enabling cancellation via task.stop.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/swarm_sdk/swarm.rb', line 285 def execute(prompt, wait: true, &block) raise ConfigurationError, "No lead agent set. Set lead= first." unless @lead_agent logs = [] current_prompt = prompt has_logging = block_given? # Save original Fiber storage for restoration (preserves parent context for nested swarms) original_fiber_storage = { execution_id: Fiber[:execution_id], swarm_id: Fiber[:swarm_id], parent_swarm_id: Fiber[:parent_swarm_id], } # Set fiber-local execution context # Use ||= to inherit parent's execution_id if one exists (for mini-swarms) Fiber[:execution_id] ||= generate_execution_id Fiber[:swarm_id] = @swarm_id Fiber[:parent_swarm_id] = @parent_swarm_id # Setup logging FIRST if block given (so swarm_start event can be emitted) setup_logging(logs, &block) if has_logging # Setup observer execution if any observers configured # MUST happen AFTER setup_logging (which clears Fiber[:log_subscriptions]) setup_observer_execution if @observer_configs.any? # Trigger swarm_start hooks (before any execution) current_prompt = apply_swarm_start_hooks(current_prompt) # Trigger first_message hooks on first execution unless @first_message_sent (current_prompt) @first_message_sent = true end # Lazy initialization of agents (with optional logging) initialize_agents unless @agents_initialized # Emit agent_start events if agents were initialized before logging was set up emit_retroactive_agent_start_events if has_logging # Delegate to Executor for actual execution executor = Executor.new(self) @current_task = executor.run( current_prompt, wait: wait, logs: logs, has_logging: has_logging, original_fiber_storage: original_fiber_storage, ) end |
#first_message_sent? ⇒ Boolean
Check if first message has been sent (for system reminder injection)
89 90 91 |
# File 'lib/swarm_sdk/swarm.rb', line 89 def @first_message_sent end |
#lead=(name) ⇒ self
Set the lead agent (entry point for swarm execution)
251 252 253 254 255 256 257 258 259 |
# File 'lib/swarm_sdk/swarm.rb', line 251 def lead=(name) name = name.to_sym unless @agent_definitions.key?(name) raise ConfigurationError, "Cannot set lead: agent '#{name}' not found" end @lead_agent = name end |
#override_swarm_ids(swarm_id:, parent_swarm_id:) ⇒ void
This method returns an undefined value.
Override swarm IDs for composable swarms
Used by SwarmLoader to set hierarchical IDs when loading sub-swarms. This is called after the swarm is built to ensure proper parent/child relationships.
558 559 560 561 |
# File 'lib/swarm_sdk/swarm.rb', line 558 def override_swarm_ids(swarm_id:, parent_swarm_id:) @swarm_id = swarm_id @parent_swarm_id = parent_swarm_id end |
#primary_agents ⇒ Object
Implement Snapshotable interface
411 412 413 |
# File 'lib/swarm_sdk/swarm.rb', line 411 def primary_agents @agents end |
#register_hook(name, &block) ⇒ self
Register a named hook that can be referenced in agent configurations
Named hooks are stored in the registry and can be referenced by symbol in agent YAML configurations or programmatically.
435 436 437 438 |
# File 'lib/swarm_sdk/swarm.rb', line 435 def register_hook(name, &block) @hook_registry.register(name, &block) self end |
#reset_context! ⇒ void
This method returns an undefined value.
Reset context for all agents
Clears conversation history for all agents. This is used by composable swarms to reset sub-swarm context when keep_context: false is specified.
446 447 448 449 450 |
# File 'lib/swarm_sdk/swarm.rb', line 446 def reset_context! @agents.each_value do |agent_chat| agent_chat.clear_conversation if agent_chat.respond_to?(:clear_conversation) end end |
#restore(snapshot, preserve_system_prompts: false) ⇒ RestoreResult
Restore conversation state from snapshot
Accepts a Snapshot object, hash, or JSON string. Validates compatibility between snapshot and current swarm configuration, restores agent conversations, context state, scratchpad, and read tracking. Returns RestoreResult with warnings about any agents that couldn't be restored due to configuration mismatches.
The swarm must be created with the SAME configuration (agent definitions, tools, prompts) as when the snapshot was created. Only conversation state is restored from the snapshot.
Restore swarm state from snapshot
By default, uses current system prompts from agent definitions (YAML + SDK defaults + plugin injections). Set preserve_system_prompts: true to use historical prompts from snapshot.
546 547 548 |
# File 'lib/swarm_sdk/swarm.rb', line 546 def restore(snapshot, preserve_system_prompts: false) StateRestorer.new(self, snapshot, preserve_system_prompts: preserve_system_prompts).restore end |
#scratchpad_enabled? ⇒ Boolean
Check if scratchpad tools are enabled
81 82 83 |
# File 'lib/swarm_sdk/swarm.rb', line 81 def scratchpad_enabled? @scratchpad_mode == :enabled end |
#snapshot ⇒ Snapshot
Create snapshot of current conversation state
Returns a Snapshot object containing:
- All agent conversations (@messages arrays)
- Agent context state (warnings, compression, TodoWrite tracking, skills)
- Delegation instance conversations
- Scratchpad contents (volatile shared storage)
- Read tracking state (which files each agent has read with digests)
- Memory read tracking state (which memory entries each agent has read with digests)
Configuration (agent definitions, tools, prompts) stays in your YAML/DSL and is NOT included in snapshots.
508 509 510 |
# File 'lib/swarm_sdk/swarm.rb', line 508 def snapshot StateSnapshot.new(self).snapshot end |
#wait_for_observers ⇒ void
This method returns an undefined value.
Wait for all observer tasks to complete
Called by Executor to wait for observer agents before cleanup. Safe to call even if no observers are configured.
470 471 472 |
# File 'lib/swarm_sdk/swarm.rb', line 470 def wait_for_observers @observer_manager&.wait_for_completion end |