swarm_sdk 2.7.13 → 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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +43 -22
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +6 -0
  4. data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
  5. data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +3 -4
  6. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  7. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  8. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  9. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  10. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  11. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  12. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  13. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  14. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  15. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  16. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  17. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  18. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  19. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  20. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  24. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  25. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  26. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  27. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  28. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  29. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  30. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  31. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  32. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  33. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  34. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  35. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  36. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  37. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  38. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  39. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  40. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  41. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  42. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  43. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  45. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  46. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  47. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  48. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  49. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  50. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  51. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  52. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  53. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  54. data/lib/swarm_sdk/v3.rb +145 -0
  55. metadata +84 -148
  56. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  57. data/lib/swarm_sdk/agent/builder.rb +0 -680
  58. data/lib/swarm_sdk/agent/chat.rb +0 -1432
  59. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  60. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  61. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  62. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  63. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  64. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  65. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  66. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  67. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  68. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  69. data/lib/swarm_sdk/agent/context.rb +0 -115
  70. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  71. data/lib/swarm_sdk/agent/definition.rb +0 -581
  72. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  73. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  74. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  75. data/lib/swarm_sdk/agent_registry.rb +0 -146
  76. data/lib/swarm_sdk/builders/base_builder.rb +0 -553
  77. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  78. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  79. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  80. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  81. data/lib/swarm_sdk/config.rb +0 -367
  82. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  83. data/lib/swarm_sdk/configuration/translator.rb +0 -283
  84. data/lib/swarm_sdk/configuration.rb +0 -165
  85. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  86. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  87. data/lib/swarm_sdk/context_compactor.rb +0 -335
  88. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  89. data/lib/swarm_sdk/context_management/context.rb +0 -328
  90. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  91. data/lib/swarm_sdk/defaults.rb +0 -251
  92. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  93. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  94. data/lib/swarm_sdk/hooks/context.rb +0 -197
  95. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  96. data/lib/swarm_sdk/hooks/error.rb +0 -29
  97. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  98. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  99. data/lib/swarm_sdk/hooks/result.rb +0 -150
  100. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  101. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  102. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  103. data/lib/swarm_sdk/log_collector.rb +0 -227
  104. data/lib/swarm_sdk/log_stream.rb +0 -127
  105. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  106. data/lib/swarm_sdk/model_aliases.json +0 -8
  107. data/lib/swarm_sdk/models.json +0 -44002
  108. data/lib/swarm_sdk/models.rb +0 -161
  109. data/lib/swarm_sdk/node_context.rb +0 -245
  110. data/lib/swarm_sdk/observer/builder.rb +0 -81
  111. data/lib/swarm_sdk/observer/config.rb +0 -45
  112. data/lib/swarm_sdk/observer/manager.rb +0 -236
  113. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  114. data/lib/swarm_sdk/permissions/config.rb +0 -239
  115. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  116. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  117. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  118. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  119. data/lib/swarm_sdk/plugin.rb +0 -309
  120. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  121. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  122. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  123. data/lib/swarm_sdk/restore_result.rb +0 -65
  124. data/lib/swarm_sdk/result.rb +0 -212
  125. data/lib/swarm_sdk/snapshot.rb +0 -156
  126. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  127. data/lib/swarm_sdk/state_restorer.rb +0 -476
  128. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  129. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  130. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -195
  131. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  132. data/lib/swarm_sdk/swarm/executor.rb +0 -290
  133. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -151
  134. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  135. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -360
  136. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -270
  137. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  138. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  139. data/lib/swarm_sdk/swarm.rb +0 -843
  140. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  141. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  142. data/lib/swarm_sdk/tools/base.rb +0 -63
  143. data/lib/swarm_sdk/tools/bash.rb +0 -280
  144. data/lib/swarm_sdk/tools/clock.rb +0 -46
  145. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  146. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  147. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  148. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  149. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  150. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  151. data/lib/swarm_sdk/tools/edit.rb +0 -145
  152. data/lib/swarm_sdk/tools/glob.rb +0 -166
  153. data/lib/swarm_sdk/tools/grep.rb +0 -235
  154. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  155. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  156. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  157. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  158. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  159. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  160. data/lib/swarm_sdk/tools/read.rb +0 -261
  161. data/lib/swarm_sdk/tools/registry.rb +0 -205
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  165. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  166. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  167. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  168. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  169. data/lib/swarm_sdk/tools/think.rb +0 -100
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  182. data/lib/swarm_sdk/workflow.rb +0 -589
  183. data/lib/swarm_sdk.rb +0 -718
@@ -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