swarm_sdk 2.7.14 → 3.0.0.alpha2

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 (185) 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/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -1,328 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module ContextManagement
5
- # Rich context wrapper for context management handlers
6
- #
7
- # Provides a clean, developer-friendly API for manipulating the conversation
8
- # context when warning thresholds are triggered. Wraps the lower-level
9
- # Hooks::Context with message manipulation helpers.
10
- #
11
- # @example Basic usage in handler
12
- # on :warning_60 do |ctx|
13
- # ctx.compress_tool_results(keep_recent: 10)
14
- # end
15
- #
16
- # @example Advanced usage with metrics
17
- # on :warning_80 do |ctx|
18
- # if ctx.usage_percentage > 85
19
- # ctx.prune_old_messages(keep_recent: 10)
20
- # ctx.log_action("aggressive_pruning", remaining: ctx.tokens_remaining)
21
- # else
22
- # ctx.compress_tool_results(keep_recent: 5, truncate_to: 100)
23
- # end
24
- # end
25
- class Context
26
- # Create a new context wrapper
27
- #
28
- # @param hooks_context [Hooks::Context] Lower-level hook context with metadata
29
- def initialize(hooks_context)
30
- @hooks_context = hooks_context
31
- @chat = hooks_context.metadata[:chat]
32
- end
33
-
34
- # --- Context Metrics ---
35
-
36
- # Current context usage percentage
37
- #
38
- # @return [Float] Usage percentage (0.0 to 100.0)
39
- #
40
- # @example
41
- # if ctx.usage_percentage > 85
42
- # ctx.prune_old_messages(keep_recent: 10)
43
- # end
44
- def usage_percentage
45
- @hooks_context.metadata[:percentage]
46
- end
47
-
48
- # Threshold that triggered this handler
49
- #
50
- # @return [Integer] Threshold (60, 80, or 90)
51
- #
52
- # @example
53
- # ctx.log_action("threshold_hit", threshold: ctx.threshold)
54
- def threshold
55
- @hooks_context.metadata[:threshold]
56
- end
57
-
58
- # Total tokens used so far
59
- #
60
- # @return [Integer] Token count
61
- #
62
- # @example
63
- # ctx.log_action("usage", tokens: ctx.tokens_used)
64
- def tokens_used
65
- @hooks_context.metadata[:tokens_used]
66
- end
67
-
68
- # Tokens remaining in context window
69
- #
70
- # @return [Integer] Token count
71
- #
72
- # @example
73
- # if ctx.tokens_remaining < 10000
74
- # ctx.prune_old_messages(keep_recent: 5)
75
- # end
76
- def tokens_remaining
77
- @hooks_context.metadata[:tokens_remaining]
78
- end
79
-
80
- # Total context window size
81
- #
82
- # @return [Integer] Token count
83
- #
84
- # @example
85
- # buffer = ctx.context_limit * 0.1 # 10% buffer
86
- def context_limit
87
- @hooks_context.metadata[:context_limit]
88
- end
89
-
90
- # Agent name
91
- #
92
- # @return [Symbol] Agent identifier
93
- #
94
- # @example
95
- # ctx.log_action("agent_context", agent: ctx.agent_name)
96
- def agent_name
97
- @hooks_context.agent_name
98
- end
99
-
100
- # --- Message Access ---
101
-
102
- # Get all messages (copy for manipulation)
103
- #
104
- # @return [Array<RubyLLM::Message>] Message array
105
- #
106
- # @example
107
- # ctx.messages.each do |msg|
108
- # puts "#{msg.role}: #{msg.content.length} chars"
109
- # end
110
- def messages
111
- @chat.messages
112
- end
113
-
114
- # Number of messages
115
- #
116
- # @return [Integer] Message count
117
- #
118
- # @example
119
- # if ctx.message_count > 100
120
- # ctx.prune_old_messages(keep_recent: 50)
121
- # end
122
- def message_count
123
- @chat.message_count
124
- end
125
-
126
- # --- Message Manipulation ---
127
-
128
- # Replace all messages with new array
129
- #
130
- # @param new_messages [Array<RubyLLM::Message>] New message array
131
- # @return [void]
132
- #
133
- # @example
134
- # new_msgs = ctx.messages.reject { |m| m.role == :tool }
135
- # ctx.replace_messages(new_msgs)
136
- def replace_messages(new_messages)
137
- @chat.replace_messages(new_messages)
138
- end
139
-
140
- # Compress tool result messages to save context space
141
- #
142
- # Creates NEW message objects with truncated content (follows RubyLLM patterns).
143
- # Truncates old tool results while keeping recent ones intact.
144
- # Automatically marks compression as applied to prevent double compression.
145
- #
146
- # @param keep_recent [Integer] Number of recent tool results to preserve (default: 10)
147
- # @param truncate_to [Integer] Max characters for truncated results (default: 200)
148
- # @return [Integer] Number of messages compressed
149
- #
150
- # @example Light compression at 60%
151
- # ctx.compress_tool_results(keep_recent: 15, truncate_to: 500)
152
- #
153
- # @example Aggressive compression at 80%
154
- # ctx.compress_tool_results(keep_recent: 5, truncate_to: 100)
155
- def compress_tool_results(keep_recent: 10, truncate_to: 200)
156
- msgs = messages.dup
157
- compressed_count = 0
158
-
159
- # Find tool result messages (skip recent ones)
160
- tool_indices = []
161
- msgs.each_with_index do |msg, idx|
162
- tool_indices << idx if msg.role == :tool
163
- end
164
-
165
- # Keep recent tool results, compress older ones
166
- indices_to_compress = tool_indices[0...-keep_recent] || []
167
-
168
- indices_to_compress.each do |idx|
169
- msg = msgs[idx]
170
- content = msg.content.to_s
171
- next if content.length <= truncate_to
172
-
173
- # Create NEW message with truncated content (NO instance_variable_set!)
174
- truncated_content = "#{content[0...truncate_to]}... [truncated for context management]"
175
-
176
- # Create new message object following RubyLLM patterns
177
- msgs[idx] = RubyLLM::Message.new(
178
- role: :tool,
179
- content: truncated_content,
180
- tool_call_id: msg.tool_call_id,
181
- )
182
- compressed_count += 1
183
- end
184
-
185
- replace_messages(msgs)
186
-
187
- # Mark compression as applied to coordinate with ContextManager
188
- mark_compression_applied
189
-
190
- compressed_count
191
- end
192
-
193
- # Mark compression as applied in ContextManager
194
- #
195
- # Call this when your handler performs compression to prevent
196
- # double compression from auto-compression logic.
197
- #
198
- # @return [void]
199
- #
200
- # @example Custom compression
201
- # msgs = ctx.messages.map { |m| ... } # custom logic
202
- # ctx.replace_messages(msgs)
203
- # ctx.mark_compression_applied
204
- def mark_compression_applied
205
- return unless @chat.respond_to?(:context_manager)
206
-
207
- @chat.context_manager.compression_applied = true
208
- end
209
-
210
- # Check if compression has already been applied
211
- #
212
- # @return [Boolean] True if compression was already applied
213
- #
214
- # @example Conditional compression
215
- # unless ctx.compression_applied?
216
- # ctx.compress_tool_results(keep_recent: 10)
217
- # end
218
- def compression_applied?
219
- return false unless @chat.respond_to?(:context_manager)
220
-
221
- !!@chat.context_manager.compression_applied
222
- end
223
-
224
- # Remove old messages from history
225
- #
226
- # Keeps system message (if any) and recent exchanges.
227
- # This is more aggressive than compression and loses context.
228
- #
229
- # @param keep_recent [Integer] Number of recent messages to keep (default: 20)
230
- # @return [Integer] Number of messages removed
231
- #
232
- # @example Prune at 80% threshold
233
- # ctx.prune_old_messages(keep_recent: 30)
234
- #
235
- # @example Emergency pruning at 90%
236
- # ctx.prune_old_messages(keep_recent: 10)
237
- def prune_old_messages(keep_recent: 20)
238
- msgs = messages.dup
239
- original_count = msgs.size
240
-
241
- # Always keep system message if present
242
- system_msg = msgs.first if msgs.first&.role == :system
243
- non_system = system_msg ? msgs[1..] : msgs
244
-
245
- # Keep only recent messages
246
- if non_system.size > keep_recent
247
- kept = non_system.last(keep_recent)
248
- new_msgs = system_msg ? [system_msg] + kept : kept
249
- replace_messages(new_msgs)
250
- original_count - new_msgs.size
251
- else
252
- 0
253
- end
254
- end
255
-
256
- # Summarize old message exchanges
257
- #
258
- # Groups old user/assistant pairs and replaces with summary.
259
- # This is a placeholder - actual implementation would use LLM.
260
- #
261
- # @param older_than [Integer] Messages older than this index get summarized
262
- # @return [Integer] Number of exchanges summarized
263
- #
264
- # @example
265
- # ctx.summarize_old_exchanges(older_than: 10)
266
- def summarize_old_exchanges(older_than: 10)
267
- # For now, this is a marker - full implementation would call LLM
268
- # to summarize exchanges. We provide the API for developers to
269
- # implement their own summarization logic.
270
- 0
271
- end
272
-
273
- # Custom message transformation
274
- #
275
- # Apply a block to transform messages. This gives full control
276
- # over message manipulation for custom strategies.
277
- #
278
- # @yield [Array<RubyLLM::Message>] Current messages
279
- # @yieldreturn [Array<RubyLLM::Message>] Transformed messages
280
- # @return [void]
281
- #
282
- # @example Remove specific tool results
283
- # ctx.transform_messages do |msgs|
284
- # msgs.reject { |m| m.role == :tool && m.content.include?("verbose output") }
285
- # end
286
- #
287
- # @example Custom compression logic
288
- # ctx.transform_messages do |msgs|
289
- # msgs.map do |m|
290
- # if m.role == :tool && m.content.length > 1000
291
- # RubyLLM::Message.new(role: :tool, content: m.content[0..500], tool_call_id: m.tool_call_id)
292
- # else
293
- # m
294
- # end
295
- # end
296
- # end
297
- def transform_messages
298
- new_msgs = yield(messages.dup)
299
- replace_messages(new_msgs)
300
- end
301
-
302
- # Log a context management action
303
- #
304
- # Emits a log event for tracking what actions were taken.
305
- # Useful for debugging and monitoring context management strategies.
306
- #
307
- # @param action [String] Description of action taken
308
- # @param details [Hash] Additional details
309
- # @return [void]
310
- #
311
- # @example Log compression action
312
- # ctx.log_action("compressed_tool_results", count: 5)
313
- #
314
- # @example Log emergency action
315
- # ctx.log_action("emergency_pruning", remaining: ctx.tokens_remaining)
316
- def log_action(action, details = {})
317
- LogStream.emit(
318
- type: "context_management_action",
319
- agent: agent_name,
320
- threshold: threshold,
321
- action: action,
322
- usage_percentage: usage_percentage,
323
- **details,
324
- )
325
- end
326
- end
327
- end
328
- end
@@ -1,226 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "delegate"
4
-
5
- module SwarmSDK
6
- # Registry for user-defined custom tools
7
- #
8
- # Provides a simple way to register custom tools without creating a full plugin.
9
- # Custom tools are registered globally and available to all agents that request them.
10
- #
11
- # ## When to Use Custom Tools vs Plugins
12
- #
13
- # **Use Custom Tools when:**
14
- # - You have simple, stateless tools
15
- # - Tools don't need persistent storage
16
- # - Tools don't need lifecycle hooks
17
- # - Tools don't need system prompt contributions
18
- #
19
- # **Use Plugins when:**
20
- # - Tools need persistent storage per agent
21
- # - Tools need lifecycle hooks (on_agent_initialized, on_user_message, etc.)
22
- # - Tools need to contribute to system prompts
23
- # - You have a suite of related tools that share configuration
24
- #
25
- # @example Register a simple tool
26
- # class WeatherTool < RubyLLM::Tool
27
- # description "Get weather for a city"
28
- # param :city, type: "string", required: true
29
- #
30
- # def execute(city:)
31
- # "Weather in #{city}: Sunny"
32
- # end
33
- # end
34
- #
35
- # SwarmSDK.register_tool(WeatherTool)
36
- #
37
- # @example Register with explicit name
38
- # SwarmSDK.register_tool(:Weather, WeatherTool)
39
- #
40
- # @example Tool with creation requirements
41
- # class AgentAwareTool < RubyLLM::Tool
42
- # def self.creation_requirements
43
- # [:agent_name, :directory]
44
- # end
45
- #
46
- # def initialize(agent_name:, directory:)
47
- # super()
48
- # @agent_name = agent_name
49
- # @directory = directory
50
- # end
51
- #
52
- # def execute
53
- # "Agent: #{@agent_name}, Dir: #{@directory}"
54
- # end
55
- # end
56
- #
57
- # SwarmSDK.register_tool(AgentAwareTool)
58
- #
59
- module CustomToolRegistry
60
- # Wrapper that overrides the tool's name to match the registered name
61
- #
62
- # This ensures that when a user registers a tool with a specific name,
63
- # that name is what gets used for tool lookup (has_tool?) and LLM tool calls.
64
- class NamedToolWrapper < SimpleDelegator
65
- def initialize(tool, registered_name)
66
- super(tool)
67
- @registered_name = registered_name.to_s
68
- end
69
-
70
- # Override name to return the registered name
71
- def name
72
- @registered_name
73
- end
74
- end
75
-
76
- @tools = {}
77
-
78
- class << self
79
- # Register a custom tool
80
- #
81
- # @param name [Symbol] Tool name
82
- # @param tool_class [Class] Tool class (must be a RubyLLM::Tool subclass)
83
- # @raise [ArgumentError] If tool_class is not a RubyLLM::Tool subclass
84
- # @raise [ArgumentError] If a tool with the same name is already registered
85
- # @return [void]
86
- def register(name, tool_class)
87
- name = name.to_sym
88
-
89
- unless tool_class.is_a?(Class) && tool_class < RubyLLM::Tool
90
- raise ArgumentError, "Tool class must inherit from RubyLLM::Tool"
91
- end
92
-
93
- if @tools.key?(name)
94
- raise ArgumentError, "Custom tool '#{name}' is already registered"
95
- end
96
-
97
- if PluginRegistry.plugin_tool?(name)
98
- raise ArgumentError, "Tool '#{name}' is already provided by a plugin"
99
- end
100
-
101
- if Tools::Registry.exists?(name)
102
- raise ArgumentError, "Tool '#{name}' is a built-in tool and cannot be overridden"
103
- end
104
-
105
- @tools[name] = tool_class
106
- end
107
-
108
- # Check if a custom tool is registered
109
- #
110
- # @param name [Symbol, String] Tool name
111
- # @return [Boolean]
112
- def registered?(name)
113
- @tools.key?(name.to_sym)
114
- end
115
-
116
- # Get a registered tool class
117
- #
118
- # @param name [Symbol, String] Tool name
119
- # @return [Class, nil] Tool class or nil if not found
120
- def get(name)
121
- @tools[name.to_sym]
122
- end
123
-
124
- # Get all registered custom tool names
125
- #
126
- # @return [Array<Symbol>]
127
- def tool_names
128
- @tools.keys
129
- end
130
-
131
- # Create a tool instance
132
- #
133
- # Uses the tool's `creation_requirements` class method (if defined) to determine
134
- # what parameters to pass to the constructor. The created tool is wrapped with
135
- # NamedToolWrapper to ensure the registered name is used for tool lookup.
136
- #
137
- # @param name [Symbol, String] Tool name
138
- # @param context [Hash] Available context for tool creation
139
- # @option context [Symbol] :agent_name Agent identifier
140
- # @option context [String] :directory Agent's working directory
141
- # @return [RubyLLM::Tool] Instantiated tool (wrapped with registered name)
142
- # @raise [ConfigurationError] If tool is unknown or has unmet requirements
143
- def create(name, context = {})
144
- name_sym = name.to_sym
145
- tool_class = @tools[name_sym]
146
-
147
- raise ConfigurationError, "Unknown custom tool: #{name}" unless tool_class
148
-
149
- # Create the tool instance
150
- tool = if tool_class.respond_to?(:creation_requirements)
151
- requirements = tool_class.creation_requirements
152
- params = extract_params(requirements, context, name)
153
- tool_class.new(**params)
154
- else
155
- # No requirements - simple instantiation
156
- tool_class.new
157
- end
158
-
159
- # Wrap with NamedToolWrapper to ensure registered name is used
160
- NamedToolWrapper.new(tool, name_sym)
161
- end
162
-
163
- # Unregister a custom tool
164
- #
165
- # @param name [Symbol, String] Tool name
166
- # @return [Class, nil] The unregistered tool class, or nil if not found
167
- def unregister(name)
168
- @tools.delete(name.to_sym)
169
- end
170
-
171
- # Clear all registered custom tools
172
- #
173
- # Primarily useful for testing.
174
- #
175
- # @return [void]
176
- def clear
177
- @tools.clear
178
- end
179
-
180
- # Infer tool name from class name
181
- #
182
- # @param tool_class [Class] Tool class
183
- # @return [Symbol] Inferred tool name
184
- #
185
- # @example
186
- # infer_name(WeatherTool) #=> :Weather
187
- # infer_name(MyApp::Tools::StockPrice) #=> :StockPrice
188
- # infer_name(MyApp::Tools::StockPriceTool) #=> :StockPrice
189
- def infer_name(tool_class)
190
- # Get the class name without module prefix
191
- class_name = tool_class.name.split("::").last
192
-
193
- # Remove "Tool" suffix if present
194
- name = class_name.sub(/Tool\z/, "")
195
-
196
- name.to_sym
197
- end
198
-
199
- private
200
-
201
- # Extract required parameters from context
202
- #
203
- # @param requirements [Array<Symbol>] Required parameter names
204
- # @param context [Hash] Available context
205
- # @param tool_name [Symbol] Tool name for error messages
206
- # @return [Hash] Parameters to pass to tool constructor
207
- # @raise [ConfigurationError] If required parameter is missing
208
- def extract_params(requirements, context, tool_name)
209
- params = {}
210
-
211
- requirements.each do |req|
212
- unless context.key?(req)
213
- raise ConfigurationError,
214
- "Custom tool '#{tool_name}' requires '#{req}' but it was not provided. " \
215
- "Ensure the tool's `creation_requirements` only includes supported keys: " \
216
- ":agent_name, :directory"
217
- end
218
-
219
- params[req] = context[req]
220
- end
221
-
222
- params
223
- end
224
- end
225
- end
226
- end