swarm_sdk 2.7.14 → 3.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  42. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  43. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  44. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  45. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  46. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  47. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  48. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  49. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  50. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  51. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  52. data/lib/swarm_sdk/v3.rb +145 -0
  53. metadata +83 -148
  54. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  55. data/lib/swarm_sdk/agent/builder.rb +0 -705
  56. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  57. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  58. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  59. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  60. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  61. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  62. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  63. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  64. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  65. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  66. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  67. data/lib/swarm_sdk/agent/context.rb +0 -115
  68. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  69. data/lib/swarm_sdk/agent/definition.rb +0 -588
  70. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  71. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  72. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  73. data/lib/swarm_sdk/agent_registry.rb +0 -146
  74. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  75. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  76. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  77. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  78. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  79. data/lib/swarm_sdk/config.rb +0 -368
  80. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  81. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  82. data/lib/swarm_sdk/configuration.rb +0 -165
  83. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  84. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  85. data/lib/swarm_sdk/context_compactor.rb +0 -335
  86. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  87. data/lib/swarm_sdk/context_management/context.rb +0 -328
  88. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  89. data/lib/swarm_sdk/defaults.rb +0 -251
  90. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  91. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  92. data/lib/swarm_sdk/hooks/context.rb +0 -197
  93. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  94. data/lib/swarm_sdk/hooks/error.rb +0 -29
  95. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  96. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  97. data/lib/swarm_sdk/hooks/result.rb +0 -150
  98. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  99. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  100. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  101. data/lib/swarm_sdk/log_collector.rb +0 -227
  102. data/lib/swarm_sdk/log_stream.rb +0 -127
  103. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  104. data/lib/swarm_sdk/model_aliases.json +0 -8
  105. data/lib/swarm_sdk/models.json +0 -44002
  106. data/lib/swarm_sdk/models.rb +0 -161
  107. data/lib/swarm_sdk/node_context.rb +0 -245
  108. data/lib/swarm_sdk/observer/builder.rb +0 -81
  109. data/lib/swarm_sdk/observer/config.rb +0 -45
  110. data/lib/swarm_sdk/observer/manager.rb +0 -248
  111. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  112. data/lib/swarm_sdk/permissions/config.rb +0 -239
  113. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  114. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  115. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  116. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  117. data/lib/swarm_sdk/plugin.rb +0 -309
  118. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  119. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  120. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  121. data/lib/swarm_sdk/restore_result.rb +0 -65
  122. data/lib/swarm_sdk/result.rb +0 -241
  123. data/lib/swarm_sdk/snapshot.rb +0 -156
  124. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  125. data/lib/swarm_sdk/state_restorer.rb +0 -476
  126. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  127. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  128. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  129. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  130. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  131. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  132. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  133. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  134. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  135. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  136. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  137. data/lib/swarm_sdk/swarm.rb +0 -973
  138. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  139. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  140. data/lib/swarm_sdk/tools/base.rb +0 -63
  141. data/lib/swarm_sdk/tools/bash.rb +0 -280
  142. data/lib/swarm_sdk/tools/clock.rb +0 -46
  143. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  144. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  145. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  146. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  147. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  148. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  149. data/lib/swarm_sdk/tools/edit.rb +0 -145
  150. data/lib/swarm_sdk/tools/glob.rb +0 -166
  151. data/lib/swarm_sdk/tools/grep.rb +0 -235
  152. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  153. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  154. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  155. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  156. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  157. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  158. data/lib/swarm_sdk/tools/read.rb +0 -261
  159. data/lib/swarm_sdk/tools/registry.rb +0 -205
  160. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  161. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  163. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  164. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  165. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  166. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  167. data/lib/swarm_sdk/tools/think.rb +0 -100
  168. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  169. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  170. data/lib/swarm_sdk/tools/write.rb +0 -112
  171. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  172. data/lib/swarm_sdk/utils.rb +0 -68
  173. data/lib/swarm_sdk/validation_result.rb +0 -33
  174. data/lib/swarm_sdk/version.rb +0 -5
  175. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  176. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  177. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  178. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  179. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  180. data/lib/swarm_sdk/workflow.rb +0 -589
  181. data/lib/swarm_sdk.rb +0 -721
@@ -1,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Models provides model validation and suggestion functionality
5
- #
6
- # Uses static JSON files:
7
- # - models.json: Curated model list from Parsera
8
- # - model_aliases.json: Shortcuts mapping to latest models
9
- #
10
- # This avoids network calls, API key requirements, and RubyLLM
11
- # registry manipulation.
12
- #
13
- # @example
14
- # model = SwarmSDK::Models.find("claude-sonnet-4-5-20250929")
15
- # model = SwarmSDK::Models.find("sonnet") # Uses alias
16
- # suggestions = SwarmSDK::Models.suggest_similar("anthropic:claude-sonnet-4-5")
17
- class Models
18
- MODELS_JSON_PATH = File.expand_path("models.json", __dir__)
19
- ALIASES_JSON_PATH = File.expand_path("model_aliases.json", __dir__)
20
-
21
- # Model information wrapper providing method access to model data
22
- #
23
- # Wraps the raw Hash from models.json to provide RubyLLM::Model::Info-like
24
- # interface for compatibility with code expecting method access.
25
- #
26
- # @example
27
- # model = SwarmSDK::Models.find("claude-sonnet-4-5-20250929")
28
- # model.context_window #=> 200000
29
- # model.id #=> "claude-sonnet-4-5-20250929"
30
- class ModelInfo
31
- attr_reader :id,
32
- :name,
33
- :provider,
34
- :family,
35
- :context_window,
36
- :max_output_tokens,
37
- :knowledge_cutoff,
38
- :modalities,
39
- :capabilities,
40
- :pricing,
41
- :metadata
42
-
43
- # Create a ModelInfo from a Hash
44
- #
45
- # @param data [Hash] Model data from models.json
46
- def initialize(data)
47
- @id = data["id"] || data[:id]
48
- @name = data["name"] || data[:name]
49
- @provider = data["provider"] || data[:provider]
50
- @family = data["family"] || data[:family]
51
- @context_window = data["context_window"] || data[:context_window]
52
- @max_output_tokens = data["max_output_tokens"] || data[:max_output_tokens]
53
- @knowledge_cutoff = data["knowledge_cutoff"] || data[:knowledge_cutoff]
54
- @modalities = data["modalities"] || data[:modalities]
55
- @capabilities = data["capabilities"] || data[:capabilities]
56
- @pricing = data["pricing"] || data[:pricing]
57
- @metadata = data["metadata"] || data[:metadata]
58
- end
59
- end
60
-
61
- class << self
62
- # Find a model by ID or alias
63
- #
64
- # @param model_id [String] Model ID or alias to find
65
- # @return [ModelInfo, nil] Model info or nil if not found
66
- def find(model_id)
67
- # Check if it's an alias first
68
- resolved_id = resolve_alias(model_id)
69
-
70
- model_hash = all.find { |m| m["id"] == resolved_id || m[:id] == resolved_id }
71
- model_hash ? ModelInfo.new(model_hash) : nil
72
- end
73
-
74
- # Resolve a model alias to full model ID
75
- #
76
- # @param model_id [String] Model ID or alias
77
- # @return [String] Resolved model ID (or original if not an alias)
78
- def resolve_alias(model_id)
79
- aliases[model_id.to_s] || model_id
80
- end
81
-
82
- # Suggest similar models for a given query
83
- #
84
- # Strips provider prefixes and normalizes for fuzzy matching.
85
- #
86
- # @param query [String] Model ID to match against
87
- # @param limit [Integer] Maximum number of suggestions
88
- # @return [Array<Hash>] Up to `limit` similar models
89
- def suggest_similar(query, limit: 3)
90
- # Strip provider prefix (e.g., "anthropic:claude-sonnet-4-5" → "claude-sonnet-4-5")
91
- query_without_prefix = query.to_s.sub(/^[^:]+:/, "")
92
- normalized_query = query_without_prefix.downcase.gsub(/[.\-_]/, "")
93
-
94
- matches = all.select do |model|
95
- model_id = (model["id"] || model[:id]).to_s
96
- model_name = (model["name"] || model[:name]).to_s
97
-
98
- normalized_id = model_id.downcase.gsub(/[.\-_]/, "")
99
- normalized_name = model_name.downcase.gsub(/[.\-_]/, "")
100
-
101
- normalized_id.include?(normalized_query) || normalized_name.include?(normalized_query)
102
- end.first(limit)
103
-
104
- matches.map do |m|
105
- {
106
- id: m["id"] || m[:id],
107
- name: m["name"] || m[:name],
108
- context_window: m["context_window"] || m[:context_window],
109
- }
110
- end
111
- end
112
-
113
- # Get all models
114
- #
115
- # @return [Array<Hash>] All models from models.json
116
- def all
117
- @models ||= load_models
118
- end
119
-
120
- # Get all aliases
121
- #
122
- # @return [Hash] Alias mappings
123
- def aliases
124
- @aliases ||= load_aliases
125
- end
126
-
127
- # Reload models and aliases from JSON files
128
- #
129
- # @return [Array<Hash>] Loaded models
130
- def reload!
131
- @models = load_models
132
- @aliases = load_aliases
133
- @models
134
- end
135
-
136
- private
137
-
138
- # Load models from JSON file
139
- #
140
- # @return [Array<Hash>] Models array
141
- def load_models
142
- JSON.parse(File.read(MODELS_JSON_PATH))
143
- rescue StandardError => e
144
- # Log error and return empty array
145
- RubyLLM.logger.error("Failed to load SwarmSDK models.json: #{e.class} - #{e.message}")
146
- []
147
- end
148
-
149
- # Load aliases from JSON file
150
- #
151
- # @return [Hash] Alias mappings
152
- def load_aliases
153
- JSON.parse(File.read(ALIASES_JSON_PATH))
154
- rescue StandardError => e
155
- # Log error and return empty hash
156
- RubyLLM.logger.debug("Failed to load SwarmSDK model_aliases.json: #{e.class} - #{e.message}")
157
- {}
158
- end
159
- end
160
- end
161
- end
@@ -1,245 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # NodeContext provides context information to node transformers
5
- #
6
- # This class is passed to input and output transformers, giving them access to:
7
- # - The original user prompt
8
- # - Results from all previous nodes
9
- # - Current node metadata
10
- # - Convenience accessors for common operations
11
- #
12
- # @example Input transformer
13
- # input do |ctx|
14
- # ctx.content # Previous node's content (convenience)
15
- # ctx.original_prompt # Original user prompt
16
- # ctx.all_results[:plan] # Access any previous node
17
- # ctx.node_name # Current node name
18
- # end
19
- #
20
- # @example Output transformer
21
- # output do |ctx|
22
- # ctx.content # Current result's content (convenience)
23
- # ctx.original_prompt # Original user prompt
24
- # ctx.all_results[:plan] # Access previous nodes
25
- # end
26
- class NodeContext
27
- attr_reader :original_prompt, :all_results, :node_name, :dependencies
28
-
29
- # For input transformers: result from previous node(s)
30
- attr_reader :previous_result
31
-
32
- # For output transformers: current node's result
33
- attr_reader :result
34
-
35
- class << self
36
- # Create a NodeContext for input transformers
37
- #
38
- # @param previous_result [Result, Hash, String] Previous node's result or hash of results
39
- # @param all_results [Hash<Symbol, Result>] Results from all completed nodes
40
- # @param original_prompt [String] The original user prompt
41
- # @param node_name [Symbol] Current node name
42
- # @param dependencies [Array<Symbol>] Node dependencies
43
- # @param transformed_content [String, nil] Already-transformed content from previous output transformer
44
- # @return [NodeContext]
45
- def for_input(previous_result:, all_results:, original_prompt:, node_name:, dependencies:, transformed_content: nil)
46
- new(
47
- previous_result: previous_result,
48
- all_results: all_results,
49
- original_prompt: original_prompt,
50
- node_name: node_name,
51
- dependencies: dependencies,
52
- result: nil,
53
- transformed_content: transformed_content,
54
- )
55
- end
56
-
57
- # Create a NodeContext for output transformers
58
- #
59
- # @param result [Result] Current node's execution result
60
- # @param all_results [Hash<Symbol, Result>] Results from all completed nodes (including current)
61
- # @param original_prompt [String] The original user prompt
62
- # @param node_name [Symbol] Current node name
63
- # @return [NodeContext]
64
- def for_output(result:, all_results:, original_prompt:, node_name:)
65
- new(
66
- result: result,
67
- all_results: all_results,
68
- original_prompt: original_prompt,
69
- node_name: node_name,
70
- dependencies: [],
71
- previous_result: nil,
72
- transformed_content: nil,
73
- )
74
- end
75
- end
76
-
77
- def initialize(previous_result:, all_results:, original_prompt:, node_name:, dependencies:, result:, transformed_content:)
78
- @previous_result = previous_result
79
- @result = result
80
- @all_results = all_results
81
- @original_prompt = original_prompt
82
- @node_name = node_name
83
- @dependencies = dependencies
84
- @transformed_content = transformed_content
85
- end
86
-
87
- # Convenience accessor: Get content from previous_result or result
88
- #
89
- # For input transformers:
90
- # - Returns transformed_content if available (from previous output transformer)
91
- # - Otherwise returns previous_result.content (original content)
92
- # - Returns nil for multiple dependencies (use all_results instead)
93
- # For output transformers: returns result.content
94
- #
95
- # @return [String, nil]
96
- def content
97
- if @result
98
- # Output transformer context: return current result's content
99
- @result.content
100
- elsif @transformed_content
101
- # Input transformer with transformed content from previous output
102
- @transformed_content
103
- elsif @previous_result.respond_to?(:content)
104
- # Input transformer context with Result object (original content)
105
- @previous_result.content
106
- elsif @previous_result.is_a?(Hash)
107
- # Input transformer with multiple dependencies (hash of results)
108
- nil # No single "content" - user must pick from all_results hash
109
- else
110
- # String or other type (initial prompt, no dependencies)
111
- @previous_result.to_s
112
- end
113
- end
114
-
115
- # Convenience accessor: Get agent from previous_result or result
116
- #
117
- # @return [String, nil]
118
- def agent
119
- if @result
120
- @result.agent
121
- elsif @previous_result.respond_to?(:agent)
122
- @previous_result.agent
123
- end
124
- end
125
-
126
- # Convenience accessor: Get logs from previous_result or result
127
- #
128
- # @return [Array, nil]
129
- def logs
130
- if @result
131
- @result.logs
132
- elsif @previous_result.respond_to?(:logs)
133
- @previous_result.logs
134
- end
135
- end
136
-
137
- # Convenience accessor: Get duration from previous_result or result
138
- #
139
- # @return [Float, nil]
140
- def duration
141
- if @result
142
- @result.duration
143
- elsif @previous_result.respond_to?(:duration)
144
- @previous_result.duration
145
- end
146
- end
147
-
148
- # Convenience accessor: Get error from previous_result or result
149
- #
150
- # @return [Exception, nil]
151
- def error
152
- if @result
153
- @result.error
154
- elsif @previous_result.respond_to?(:error)
155
- @previous_result.error
156
- end
157
- end
158
-
159
- # Convenience accessor: Check success status
160
- #
161
- # @return [Boolean, nil]
162
- def success?
163
- if @result
164
- @result.success?
165
- elsif @previous_result.respond_to?(:success?)
166
- @previous_result.success?
167
- end
168
- end
169
-
170
- # Control flow methods for transformers
171
- # These return special hashes that Workflow recognizes
172
-
173
- # Skip current node's LLM execution and return content immediately
174
- #
175
- # Only valid for input transformers.
176
- #
177
- # @param content [String] Content to return (skips LLM call)
178
- # @return [Hash] Control hash for skip_execution
179
- # @raise [ArgumentError] If content is nil
180
- #
181
- # @example
182
- # input do |ctx|
183
- # cached = check_cache(ctx.content)
184
- # return ctx.skip_execution(content: cached) if cached
185
- # ctx.content
186
- # end
187
- def skip_execution(content:)
188
- if content.nil?
189
- raise ArgumentError,
190
- "skip_execution requires content (got nil). " \
191
- "Check that ctx.content or your content source is not nil. " \
192
- "Node: #{@node_name}"
193
- end
194
- { skip_execution: true, content: content }
195
- end
196
-
197
- # Halt entire workflow and return content as final result
198
- #
199
- # Valid for both input and output transformers.
200
- #
201
- # @param content [String] Final content to return
202
- # @return [Hash] Control hash for halt_workflow
203
- # @raise [ArgumentError] If content is nil
204
- #
205
- # @example
206
- # output do |ctx|
207
- # return ctx.halt_workflow(content: ctx.content) if converged?(ctx.content)
208
- # ctx.content
209
- # end
210
- def halt_workflow(content:)
211
- if content.nil?
212
- raise ArgumentError,
213
- "halt_workflow requires content (got nil). " \
214
- "Check that ctx.content or your content source is not nil. " \
215
- "Node: #{@node_name}"
216
- end
217
- { halt_workflow: true, content: content }
218
- end
219
-
220
- # Jump to a different node with provided content as input
221
- #
222
- # Valid for both input and output transformers.
223
- #
224
- # @param node [Symbol] Node name to jump to
225
- # @param content [String] Content to pass to target node
226
- # @return [Hash] Control hash for goto_node
227
- # @raise [ArgumentError] If content is nil
228
- #
229
- # @example
230
- # input do |ctx|
231
- # return ctx.goto_node(:review, content: ctx.content) if needs_review?(ctx.content)
232
- # ctx.content
233
- # end
234
- def goto_node(node, content:)
235
- if content.nil?
236
- raise ArgumentError,
237
- "goto_node requires content (got nil). " \
238
- "Check that ctx.content or your content source is not nil. " \
239
- "This often happens when the previous node failed with an error. " \
240
- "Node: #{@node_name}, Target: #{node}"
241
- end
242
- { goto_node: node.to_sym, content: content }
243
- end
244
- end
245
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Observer
5
- # DSL for configuring observer agents
6
- #
7
- # Used by Swarm::Builder#observer to provide a clean DSL for defining
8
- # event handlers and observer configuration options.
9
- #
10
- # @example Basic usage
11
- # observer :profiler do
12
- # on :swarm_start do |event|
13
- # "Analyze this prompt: #{event[:prompt]}"
14
- # end
15
- #
16
- # timeout 120
17
- # max_concurrent 2
18
- # end
19
- class Builder
20
- # Initialize builder with agent name and config
21
- #
22
- # @param agent_name [Symbol] Name of the observer agent
23
- # @param config [Observer::Config] Configuration object to populate
24
- def initialize(agent_name, config)
25
- @agent_name = agent_name
26
- @config = config
27
- end
28
-
29
- # Register an event handler
30
- #
31
- # The block receives the event hash and should return:
32
- # - A prompt string to trigger the observer agent
33
- # - nil to skip execution for this event
34
- #
35
- # @param event_type [Symbol] Type of event to handle (e.g., :swarm_start, :tool_call)
36
- # @yield [Hash] Event hash
37
- # @yieldreturn [String, nil] Prompt or nil to skip
38
- # @return [void]
39
- #
40
- # @example
41
- # on :tool_call do |event|
42
- # next unless event[:tool_name] == "Bash"
43
- # "Check this command: #{event[:arguments][:command]}"
44
- # end
45
- def on(event_type, &block)
46
- @config.add_handler(event_type, &block)
47
- end
48
-
49
- # Set maximum concurrent executions for this observer
50
- #
51
- # Limits how many instances of this observer agent can run simultaneously.
52
- # Useful for resource-intensive observers.
53
- #
54
- # @param n [Integer] Maximum concurrent executions
55
- # @return [void]
56
- def max_concurrent(n)
57
- @config.options[:max_concurrent] = n
58
- end
59
-
60
- # Set timeout for observer execution
61
- #
62
- # Observer tasks will be cancelled after this duration.
63
- #
64
- # @param seconds [Integer] Timeout in seconds (default: 60)
65
- # @return [void]
66
- def timeout(seconds)
67
- @config.options[:timeout] = seconds
68
- end
69
-
70
- # Wait for observer to complete before swarm execution ends
71
- #
72
- # By default, observers are fire-and-forget. This option causes
73
- # the main execution to wait for this observer to complete.
74
- #
75
- # @return [void]
76
- def wait_for_completion!
77
- @config.options[:fire_and_forget] = false
78
- end
79
- end
80
- end
81
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Observer
5
- # Configuration for an observer agent
6
- #
7
- # Holds the agent name, event handlers (blocks that return prompts or nil),
8
- # and execution options.
9
- #
10
- # @example
11
- # config = Observer::Config.new(:profiler)
12
- # config.add_handler(:swarm_start) { |event| "Analyze: #{event[:prompt]}" }
13
- # config.options[:timeout] = 120
14
- class Config
15
- attr_reader :agent_name, :event_handlers, :options
16
-
17
- # Initialize a new observer configuration
18
- #
19
- # @param agent_name [Symbol] Name of the agent to use as observer
20
- def initialize(agent_name)
21
- @agent_name = agent_name
22
- @event_handlers = {} # { event_type => block }
23
- @options = {
24
- max_concurrent: nil,
25
- timeout: 60,
26
- fire_and_forget: true,
27
- }
28
- end
29
-
30
- # Add an event handler for a specific event type
31
- #
32
- # The block receives the event hash and should return:
33
- # - A prompt string to trigger the observer agent
34
- # - nil to skip execution for this event
35
- #
36
- # @param event_type [Symbol] Type of event to handle (e.g., :swarm_start, :tool_call)
37
- # @yield [Hash] Event hash with type, agent, and other data
38
- # @yieldreturn [String, nil] Prompt to execute or nil to skip
39
- # @return [void]
40
- def add_handler(event_type, &block)
41
- @event_handlers[event_type] = block
42
- end
43
- end
44
- end
45
- end