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.
- checksums.yaml +4 -4
- data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
- data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
- data/lib/swarm_sdk/v3/agent.rb +1165 -0
- data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
- data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
- data/lib/swarm_sdk/v3/configuration.rb +490 -0
- data/lib/swarm_sdk/v3/debug_log.rb +86 -0
- data/lib/swarm_sdk/v3/event_stream.rb +130 -0
- data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
- data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
- data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
- data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
- data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
- data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
- data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
- data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
- data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
- data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
- data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
- data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
- data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
- data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
- data/lib/swarm_sdk/v3/memory/card.rb +206 -0
- data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
- data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
- data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
- data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
- data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
- data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
- data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
- data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
- data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
- data/lib/swarm_sdk/v3/memory/store.rb +489 -0
- data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
- data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
- data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
- data/lib/swarm_sdk/v3/tools/base.rb +80 -0
- data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
- data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
- data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
- data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
- data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
- data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
- data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
- data/lib/swarm_sdk/v3/tools/read.rb +181 -0
- data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
- data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
- data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
- data/lib/swarm_sdk/v3/tools/think.rb +88 -0
- data/lib/swarm_sdk/v3/tools/write.rb +87 -0
- data/lib/swarm_sdk/v3.rb +145 -0
- metadata +83 -148
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
- data/lib/swarm_sdk/agent/builder.rb +0 -705
- data/lib/swarm_sdk/agent/chat.rb +0 -1438
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
- data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
- data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
- data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
- data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
- data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
- data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
- data/lib/swarm_sdk/agent/context.rb +0 -115
- data/lib/swarm_sdk/agent/context_manager.rb +0 -315
- data/lib/swarm_sdk/agent/definition.rb +0 -588
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
- data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
- data/lib/swarm_sdk/agent_registry.rb +0 -146
- data/lib/swarm_sdk/builders/base_builder.rb +0 -558
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
- data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
- data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
- data/lib/swarm_sdk/concerns/validatable.rb +0 -55
- data/lib/swarm_sdk/config.rb +0 -368
- data/lib/swarm_sdk/configuration/parser.rb +0 -397
- data/lib/swarm_sdk/configuration/translator.rb +0 -285
- data/lib/swarm_sdk/configuration.rb +0 -165
- data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
- data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
- data/lib/swarm_sdk/context_compactor.rb +0 -335
- data/lib/swarm_sdk/context_management/builder.rb +0 -128
- data/lib/swarm_sdk/context_management/context.rb +0 -328
- data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
- data/lib/swarm_sdk/defaults.rb +0 -251
- data/lib/swarm_sdk/events_to_messages.rb +0 -199
- data/lib/swarm_sdk/hooks/adapter.rb +0 -359
- data/lib/swarm_sdk/hooks/context.rb +0 -197
- data/lib/swarm_sdk/hooks/definition.rb +0 -80
- data/lib/swarm_sdk/hooks/error.rb +0 -29
- data/lib/swarm_sdk/hooks/executor.rb +0 -146
- data/lib/swarm_sdk/hooks/registry.rb +0 -147
- data/lib/swarm_sdk/hooks/result.rb +0 -150
- data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
- data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
- data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
- data/lib/swarm_sdk/log_collector.rb +0 -227
- data/lib/swarm_sdk/log_stream.rb +0 -127
- data/lib/swarm_sdk/markdown_parser.rb +0 -75
- data/lib/swarm_sdk/model_aliases.json +0 -8
- data/lib/swarm_sdk/models.json +0 -44002
- data/lib/swarm_sdk/models.rb +0 -161
- data/lib/swarm_sdk/node_context.rb +0 -245
- data/lib/swarm_sdk/observer/builder.rb +0 -81
- data/lib/swarm_sdk/observer/config.rb +0 -45
- data/lib/swarm_sdk/observer/manager.rb +0 -248
- data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
- data/lib/swarm_sdk/permissions/config.rb +0 -239
- data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
- data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
- data/lib/swarm_sdk/permissions/validator.rb +0 -173
- data/lib/swarm_sdk/permissions_builder.rb +0 -122
- data/lib/swarm_sdk/plugin.rb +0 -309
- data/lib/swarm_sdk/plugin_registry.rb +0 -101
- data/lib/swarm_sdk/proc_helpers.rb +0 -53
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
- data/lib/swarm_sdk/restore_result.rb +0 -65
- data/lib/swarm_sdk/result.rb +0 -241
- data/lib/swarm_sdk/snapshot.rb +0 -156
- data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
- data/lib/swarm_sdk/state_restorer.rb +0 -476
- data/lib/swarm_sdk/state_snapshot.rb +0 -334
- data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
- data/lib/swarm_sdk/swarm/builder.rb +0 -256
- data/lib/swarm_sdk/swarm/executor.rb +0 -446
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
- data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
- data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
- data/lib/swarm_sdk/swarm.rb +0 -973
- data/lib/swarm_sdk/swarm_loader.rb +0 -145
- data/lib/swarm_sdk/swarm_registry.rb +0 -136
- data/lib/swarm_sdk/tools/base.rb +0 -63
- data/lib/swarm_sdk/tools/bash.rb +0 -280
- data/lib/swarm_sdk/tools/clock.rb +0 -46
- data/lib/swarm_sdk/tools/delegate.rb +0 -389
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
- data/lib/swarm_sdk/tools/edit.rb +0 -145
- data/lib/swarm_sdk/tools/glob.rb +0 -166
- data/lib/swarm_sdk/tools/grep.rb +0 -235
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
- data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
- data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
- data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
- data/lib/swarm_sdk/tools/read.rb +0 -261
- data/lib/swarm_sdk/tools/registry.rb +0 -205
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
- data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
- data/lib/swarm_sdk/tools/think.rb +0 -100
- data/lib/swarm_sdk/tools/todo_write.rb +0 -237
- data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
- data/lib/swarm_sdk/tools/write.rb +0 -112
- data/lib/swarm_sdk/transcript_builder.rb +0 -278
- data/lib/swarm_sdk/utils.rb +0 -68
- data/lib/swarm_sdk/validation_result.rb +0 -33
- data/lib/swarm_sdk/version.rb +0 -5
- data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
- data/lib/swarm_sdk/workflow/builder.rb +0 -227
- data/lib/swarm_sdk/workflow/executor.rb +0 -497
- data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
- data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
- data/lib/swarm_sdk/workflow.rb +0 -589
- data/lib/swarm_sdk.rb +0 -721
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
module V3
|
|
5
|
+
# DSL builder for creating a single V3 Agent
|
|
6
|
+
#
|
|
7
|
+
# Provides a clean, block-based interface for defining an agent without
|
|
8
|
+
# needing to construct AgentDefinition kwargs directly. Unlike the V2
|
|
9
|
+
# builder which manages multi-agent swarms, this creates a single agent.
|
|
10
|
+
#
|
|
11
|
+
# @example Minimal agent
|
|
12
|
+
# agent = SwarmSDK::V3.agent do
|
|
13
|
+
# name :assistant
|
|
14
|
+
# description "A helpful assistant"
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example Full agent with memory
|
|
18
|
+
# agent = SwarmSDK::V3.agent do
|
|
19
|
+
# name :researcher
|
|
20
|
+
# description "Research specialist"
|
|
21
|
+
# model "claude-sonnet-4"
|
|
22
|
+
# provider "anthropic"
|
|
23
|
+
# system_prompt "You are an expert researcher."
|
|
24
|
+
# tools :Read, :Write, :Edit, :Bash, :Grep, :Glob
|
|
25
|
+
# directory "/path/to/project"
|
|
26
|
+
#
|
|
27
|
+
# memory do
|
|
28
|
+
# directory ".swarm/memory"
|
|
29
|
+
# stm_turns 8
|
|
30
|
+
# retrieval_top_k 15
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# agent.ask("Hello!")
|
|
35
|
+
class AgentBuilder
|
|
36
|
+
class << self
|
|
37
|
+
# Build an Agent from a DSL block
|
|
38
|
+
#
|
|
39
|
+
# @yield DSL block evaluated via instance_eval
|
|
40
|
+
# @return [Agent] Initialized agent ready for use
|
|
41
|
+
#
|
|
42
|
+
# @example
|
|
43
|
+
# agent = AgentBuilder.build do
|
|
44
|
+
# name :coder
|
|
45
|
+
# description "Code writer"
|
|
46
|
+
# tools :Read, :Edit
|
|
47
|
+
# end
|
|
48
|
+
def build(&block)
|
|
49
|
+
builder = new
|
|
50
|
+
builder.instance_eval(&block)
|
|
51
|
+
builder.to_agent
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def initialize
|
|
56
|
+
@config = {}
|
|
57
|
+
@tools = []
|
|
58
|
+
@skills = []
|
|
59
|
+
@mcp_servers = []
|
|
60
|
+
@hooks = []
|
|
61
|
+
@memory_config = nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @!group DSL Methods
|
|
65
|
+
|
|
66
|
+
# Set agent name
|
|
67
|
+
#
|
|
68
|
+
# @param value [Symbol, String] Agent identifier
|
|
69
|
+
# @return [void]
|
|
70
|
+
#
|
|
71
|
+
# @example
|
|
72
|
+
# name :researcher
|
|
73
|
+
def name(value)
|
|
74
|
+
@config[:name] = value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Set agent description
|
|
78
|
+
#
|
|
79
|
+
# @param value [String] Agent role description
|
|
80
|
+
# @return [void]
|
|
81
|
+
#
|
|
82
|
+
# @example
|
|
83
|
+
# description "Research specialist"
|
|
84
|
+
def description(value)
|
|
85
|
+
@config[:description] = value
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Set LLM model
|
|
89
|
+
#
|
|
90
|
+
# @param value [String] Model identifier
|
|
91
|
+
# @return [void]
|
|
92
|
+
#
|
|
93
|
+
# @example
|
|
94
|
+
# model "claude-sonnet-4"
|
|
95
|
+
def model(value)
|
|
96
|
+
@config[:model] = value
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Set LLM provider
|
|
100
|
+
#
|
|
101
|
+
# @param value [String] Provider name
|
|
102
|
+
# @return [void]
|
|
103
|
+
#
|
|
104
|
+
# @example
|
|
105
|
+
# provider "anthropic"
|
|
106
|
+
def provider(value)
|
|
107
|
+
@config[:provider] = value
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Set system prompt instructions
|
|
111
|
+
#
|
|
112
|
+
# @param value [String] System prompt text
|
|
113
|
+
# @return [void]
|
|
114
|
+
#
|
|
115
|
+
# @example
|
|
116
|
+
# system_prompt "You are an expert researcher."
|
|
117
|
+
def system_prompt(value)
|
|
118
|
+
@config[:system_prompt] = value
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Add tools to the agent
|
|
122
|
+
#
|
|
123
|
+
# Can be called multiple times; tools accumulate across calls.
|
|
124
|
+
#
|
|
125
|
+
# @param names [Array<Symbol, String>] Tool names
|
|
126
|
+
# @return [void]
|
|
127
|
+
#
|
|
128
|
+
# @example Single call
|
|
129
|
+
# tools :Read, :Write, :Edit
|
|
130
|
+
#
|
|
131
|
+
# @example Multiple calls accumulate
|
|
132
|
+
# tools :Read, :Write
|
|
133
|
+
# tools :Bash, :Grep
|
|
134
|
+
# # => [:Read, :Write, :Bash, :Grep]
|
|
135
|
+
def tools(*names)
|
|
136
|
+
@tools.concat(names.flatten)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Add skill directories to the agent
|
|
140
|
+
#
|
|
141
|
+
# Can be called multiple times; paths accumulate across calls.
|
|
142
|
+
# Each path should be a directory containing a SKILL.md file,
|
|
143
|
+
# or a parent directory whose children contain SKILL.md files.
|
|
144
|
+
#
|
|
145
|
+
# @param paths [Array<String>] Skill directory paths
|
|
146
|
+
# @return [void]
|
|
147
|
+
#
|
|
148
|
+
# @example Single call
|
|
149
|
+
# skills "/path/to/skills"
|
|
150
|
+
#
|
|
151
|
+
# @example Multiple calls accumulate
|
|
152
|
+
# skills "/skills/pdf"
|
|
153
|
+
# skills "/skills/csv"
|
|
154
|
+
def skills(*paths)
|
|
155
|
+
@skills.concat(paths.flatten)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Set working directory
|
|
159
|
+
#
|
|
160
|
+
# @param value [String] Directory path
|
|
161
|
+
# @return [void]
|
|
162
|
+
#
|
|
163
|
+
# @example
|
|
164
|
+
# directory "/path/to/project"
|
|
165
|
+
def directory(value)
|
|
166
|
+
@config[:directory] = value
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Set custom API endpoint
|
|
170
|
+
#
|
|
171
|
+
# @param value [String] Base URL
|
|
172
|
+
# @return [void]
|
|
173
|
+
#
|
|
174
|
+
# @example
|
|
175
|
+
# base_url "https://api.example.com"
|
|
176
|
+
def base_url(value)
|
|
177
|
+
@config[:base_url] = value
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Set maximum concurrent tool executions
|
|
181
|
+
#
|
|
182
|
+
# @param value [Integer] Max concurrent tools
|
|
183
|
+
# @return [void]
|
|
184
|
+
#
|
|
185
|
+
# @example
|
|
186
|
+
# max_concurrent_tools 5
|
|
187
|
+
def max_concurrent_tools(value)
|
|
188
|
+
@config[:max_concurrent_tools] = value
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Set raw API body parameters
|
|
192
|
+
#
|
|
193
|
+
# @param value [Hash] Parameters hash
|
|
194
|
+
# @return [void]
|
|
195
|
+
#
|
|
196
|
+
# @example
|
|
197
|
+
# parameters(temperature: 0.7, thinking: { type: "enabled", budget_tokens: 10_000 })
|
|
198
|
+
def parameters(value)
|
|
199
|
+
@config[:parameters] = value
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Set raw HTTP headers
|
|
203
|
+
#
|
|
204
|
+
# @param value [Hash] Headers hash
|
|
205
|
+
# @return [void]
|
|
206
|
+
#
|
|
207
|
+
# @example
|
|
208
|
+
# headers("anthropic-beta" => "some-feature")
|
|
209
|
+
def headers(value)
|
|
210
|
+
@config[:headers] = value
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Set output schema for structured output
|
|
214
|
+
#
|
|
215
|
+
# The schema is passed through to RubyLLM's Chat#with_schema.
|
|
216
|
+
# Can be a Hash (raw JSON Schema) or any object responding to #to_json_schema.
|
|
217
|
+
#
|
|
218
|
+
# @param value [Hash, Object] Schema definition
|
|
219
|
+
# @return [void]
|
|
220
|
+
#
|
|
221
|
+
# @example Raw JSON Schema
|
|
222
|
+
# output_schema({ type: "object", properties: { name: { type: "string" } } })
|
|
223
|
+
def output_schema(value)
|
|
224
|
+
@config[:output_schema] = value
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Set API version for OpenAI provider
|
|
228
|
+
#
|
|
229
|
+
# Controls which OpenAI API endpoint to use. Only applicable when
|
|
230
|
+
# provider is "openai". Defaults to "v1/responses" for OpenAI agents.
|
|
231
|
+
#
|
|
232
|
+
# @param value [String] API version ("v1/responses" or "v1/chat/completions")
|
|
233
|
+
# @return [void]
|
|
234
|
+
#
|
|
235
|
+
# @example Use Chat Completions API
|
|
236
|
+
# api_version "v1/chat/completions"
|
|
237
|
+
#
|
|
238
|
+
# @example Use Responses API (default for OpenAI)
|
|
239
|
+
# api_version "v1/responses"
|
|
240
|
+
def api_version(value)
|
|
241
|
+
@config[:api_version] = value
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Configure memory via a sub-block
|
|
245
|
+
#
|
|
246
|
+
# @yield DSL block evaluated on a MemoryBuilder
|
|
247
|
+
# @return [void]
|
|
248
|
+
#
|
|
249
|
+
# @example
|
|
250
|
+
# memory do
|
|
251
|
+
# directory ".swarm/memory"
|
|
252
|
+
# stm_turns 8
|
|
253
|
+
# retrieval_top_k 15
|
|
254
|
+
# end
|
|
255
|
+
def memory(&block)
|
|
256
|
+
@memory_config = MemoryBuilder.build(&block)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Register a before_ask hook
|
|
260
|
+
#
|
|
261
|
+
# Fires before {Agent#execute_turn}. Can halt (returns nil from ask)
|
|
262
|
+
# or replace the prompt.
|
|
263
|
+
#
|
|
264
|
+
# @yield [ctx] Hook block receiving a {Hooks::Context}
|
|
265
|
+
# @return [void]
|
|
266
|
+
#
|
|
267
|
+
# @example Log every prompt
|
|
268
|
+
# before_ask { |ctx| puts "Processing: #{ctx.prompt}" }
|
|
269
|
+
#
|
|
270
|
+
# @example Block certain prompts
|
|
271
|
+
# before_ask { |ctx| ctx.halt("Blocked") if ctx.prompt.include?("secret") }
|
|
272
|
+
def before_ask(&block)
|
|
273
|
+
@hooks << { event: :before_ask, block: block }
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Register an after_ask hook
|
|
277
|
+
#
|
|
278
|
+
# Fires after {Agent#execute_turn} completes. Observation only —
|
|
279
|
+
# halt and replace have no effect.
|
|
280
|
+
#
|
|
281
|
+
# @yield [ctx] Hook block receiving a {Hooks::Context}
|
|
282
|
+
# @return [void]
|
|
283
|
+
#
|
|
284
|
+
# @example Log responses
|
|
285
|
+
# after_ask { |ctx| log_response(ctx.response) }
|
|
286
|
+
def after_ask(&block)
|
|
287
|
+
@hooks << { event: :after_ask, block: block }
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Register a before_tool hook
|
|
291
|
+
#
|
|
292
|
+
# Fires before tool execution. Can halt (returns error string to LLM
|
|
293
|
+
# without executing the tool). Supports match filtering.
|
|
294
|
+
#
|
|
295
|
+
# @param match [Symbol, String, Regexp, Array, nil] Tool name matcher
|
|
296
|
+
# @yield [ctx] Hook block receiving a {Hooks::Context}
|
|
297
|
+
# @return [void]
|
|
298
|
+
#
|
|
299
|
+
# @example Block Bash tool
|
|
300
|
+
# before_tool(match: :Bash) { |ctx| ctx.halt("Bash disabled") }
|
|
301
|
+
#
|
|
302
|
+
# @example Validate Write/Edit arguments
|
|
303
|
+
# before_tool(match: [:Write, :Edit]) { |ctx| validate(ctx.tool_arguments) }
|
|
304
|
+
def before_tool(match: nil, &block)
|
|
305
|
+
@hooks << { event: :before_tool, match: compile_matcher(match), block: block }
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Register an after_tool hook
|
|
309
|
+
#
|
|
310
|
+
# Fires after tool execution. Can replace the tool result.
|
|
311
|
+
# Supports match filtering.
|
|
312
|
+
#
|
|
313
|
+
# @param match [Symbol, String, Regexp, Array, nil] Tool name matcher
|
|
314
|
+
# @yield [ctx] Hook block receiving a {Hooks::Context}
|
|
315
|
+
# @return [void]
|
|
316
|
+
#
|
|
317
|
+
# @example Sanitize Bash output
|
|
318
|
+
# after_tool(match: /Bash/) { |ctx| ctx.replace(sanitize(ctx.tool_result)) }
|
|
319
|
+
def after_tool(match: nil, &block)
|
|
320
|
+
@hooks << { event: :after_tool, match: compile_matcher(match), block: block }
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Register an on_stop hook
|
|
324
|
+
#
|
|
325
|
+
# Fires after ask() completes successfully. Observation only —
|
|
326
|
+
# the response has already been produced. Useful for logging,
|
|
327
|
+
# metrics, or cleanup.
|
|
328
|
+
#
|
|
329
|
+
# @yield [ctx] Hook block receiving a {Hooks::Context}
|
|
330
|
+
# @return [void]
|
|
331
|
+
#
|
|
332
|
+
# @example Track completions
|
|
333
|
+
# on_stop { |ctx| metrics.increment("agent.#{ctx.agent_name}.completed") }
|
|
334
|
+
def on_stop(&block)
|
|
335
|
+
@hooks << { event: :on_stop, block: block }
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Add an MCP server connection
|
|
339
|
+
#
|
|
340
|
+
# Can be called multiple times; servers accumulate across calls.
|
|
341
|
+
# Each server provides tools that the agent can invoke via MCP protocol.
|
|
342
|
+
#
|
|
343
|
+
# @param name [Symbol, String] Server identifier
|
|
344
|
+
# @param options [Hash] Server configuration options
|
|
345
|
+
# @option options [Symbol] :type Transport type (:stdio or :http)
|
|
346
|
+
# @option options [String] :command Subprocess command (stdio)
|
|
347
|
+
# @option options [Array<String>] :args Subprocess arguments (stdio)
|
|
348
|
+
# @option options [Hash] :env Subprocess environment (stdio)
|
|
349
|
+
# @option options [String] :url HTTP endpoint URL (http)
|
|
350
|
+
# @option options [Hash] :headers HTTP headers (http)
|
|
351
|
+
# @option options [Array<Symbol>] :tools Tool names to expose (nil = all)
|
|
352
|
+
# @return [void]
|
|
353
|
+
#
|
|
354
|
+
# @example HTTP server
|
|
355
|
+
# mcp_server :api,
|
|
356
|
+
# type: :http,
|
|
357
|
+
# url: "https://example.com/mcp",
|
|
358
|
+
# headers: { "Authorization" => "Bearer token" }
|
|
359
|
+
#
|
|
360
|
+
# @example Stdio server with tool filtering
|
|
361
|
+
# mcp_server :filesystem,
|
|
362
|
+
# type: :stdio,
|
|
363
|
+
# command: "npx",
|
|
364
|
+
# args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
365
|
+
# tools: [:read_file, :list_directory]
|
|
366
|
+
def mcp_server(name, **options)
|
|
367
|
+
@mcp_servers << options.merge(name: name)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# @!endgroup
|
|
371
|
+
|
|
372
|
+
# Build the Agent from collected configuration
|
|
373
|
+
#
|
|
374
|
+
# @return [Agent] Initialized agent
|
|
375
|
+
# @raise [ConfigurationError] If required fields are missing
|
|
376
|
+
def to_agent
|
|
377
|
+
definition = AgentDefinition.new(**definition_kwargs)
|
|
378
|
+
Agent.new(definition)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
private
|
|
382
|
+
|
|
383
|
+
# Assemble keyword arguments for AgentDefinition
|
|
384
|
+
#
|
|
385
|
+
# @return [Hash] Keyword arguments
|
|
386
|
+
def definition_kwargs
|
|
387
|
+
kwargs = @config.dup
|
|
388
|
+
kwargs[:tools] = @tools unless @tools.empty?
|
|
389
|
+
kwargs[:skills] = @skills unless @skills.empty?
|
|
390
|
+
kwargs[:mcp_servers] = @mcp_servers unless @mcp_servers.empty?
|
|
391
|
+
kwargs[:hooks] = @hooks unless @hooks.empty?
|
|
392
|
+
kwargs.merge!(@memory_config) if @memory_config
|
|
393
|
+
kwargs
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Compile a match specification into a normalized matcher
|
|
397
|
+
#
|
|
398
|
+
# Handles Symbol, String, Regexp, Array, and nil inputs.
|
|
399
|
+
# Arrays are recursively compiled so nested values are normalized.
|
|
400
|
+
#
|
|
401
|
+
# @param match [Symbol, String, Regexp, Array, nil] Raw match input
|
|
402
|
+
# @return [Symbol, String, Regexp, Array, nil] Compiled matcher
|
|
403
|
+
def compile_matcher(match)
|
|
404
|
+
case match
|
|
405
|
+
when nil, Symbol, String, Regexp then match
|
|
406
|
+
when Array then match.map { |m| compile_matcher(m) }
|
|
407
|
+
else
|
|
408
|
+
raise ArgumentError, "Invalid match type: #{match.class}. Use Symbol, String, Regexp, Array, or nil."
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Sub-builder for memory configuration within AgentBuilder
|
|
414
|
+
#
|
|
415
|
+
# Collects memory-related settings and returns them as a hash
|
|
416
|
+
# with the `memory_` prefix expected by AgentDefinition.
|
|
417
|
+
#
|
|
418
|
+
# @example
|
|
419
|
+
# config = MemoryBuilder.build do
|
|
420
|
+
# directory ".swarm/memory"
|
|
421
|
+
# stm_turns 8
|
|
422
|
+
# end
|
|
423
|
+
# # => { memory_directory: ".swarm/memory", memory_stm_turns: 8 }
|
|
424
|
+
class MemoryBuilder
|
|
425
|
+
class << self
|
|
426
|
+
# Build memory config from a DSL block
|
|
427
|
+
#
|
|
428
|
+
# @yield DSL block evaluated via instance_eval
|
|
429
|
+
# @return [Hash] Memory configuration with `memory_` prefixed keys
|
|
430
|
+
def build(&block)
|
|
431
|
+
builder = new
|
|
432
|
+
builder.instance_eval(&block)
|
|
433
|
+
builder.to_h
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def initialize
|
|
438
|
+
@config = {}
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Set memory storage directory
|
|
442
|
+
#
|
|
443
|
+
# @param value [String] Directory path
|
|
444
|
+
# @return [void]
|
|
445
|
+
def directory(value)
|
|
446
|
+
@config[:memory_directory] = value
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Set number of recent turns to keep in short-term memory
|
|
450
|
+
#
|
|
451
|
+
# @param value [Integer] STM turn count
|
|
452
|
+
# @return [void]
|
|
453
|
+
def stm_turns(value)
|
|
454
|
+
@config[:memory_stm_turns] = value
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Set number of memory cards to retrieve per turn
|
|
458
|
+
#
|
|
459
|
+
# @param value [Integer] Top-k retrieval count
|
|
460
|
+
# @return [void]
|
|
461
|
+
def retrieval_top_k(value)
|
|
462
|
+
@config[:memory_retrieval_top_k] = value
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# Set semantic search weight for hybrid retrieval
|
|
466
|
+
#
|
|
467
|
+
# @param value [Float] Weight between 0.0 and 1.0
|
|
468
|
+
# @return [void]
|
|
469
|
+
def semantic_weight(value)
|
|
470
|
+
@config[:memory_semantic_weight] = value
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# Set keyword search weight for hybrid retrieval
|
|
474
|
+
#
|
|
475
|
+
# @param value [Float] Weight between 0.0 and 1.0
|
|
476
|
+
# @return [void]
|
|
477
|
+
def keyword_weight(value)
|
|
478
|
+
@config[:memory_keyword_weight] = value
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Valid adapter type symbols
|
|
482
|
+
VALID_ADAPTER_TYPES = [:sqlite, :filesystem].freeze
|
|
483
|
+
|
|
484
|
+
# Set the memory adapter
|
|
485
|
+
#
|
|
486
|
+
# @param value [Symbol, Memory::Adapters::Base] Adapter type or instance
|
|
487
|
+
# @return [void]
|
|
488
|
+
# @raise [ArgumentError] If value is not a valid adapter type or instance
|
|
489
|
+
#
|
|
490
|
+
# @example Using a symbol
|
|
491
|
+
# adapter :sqlite
|
|
492
|
+
# adapter :filesystem
|
|
493
|
+
#
|
|
494
|
+
# @example Using an adapter instance
|
|
495
|
+
# adapter MyCustomAdapter.new("/path/to/storage")
|
|
496
|
+
def adapter(value)
|
|
497
|
+
case value
|
|
498
|
+
when Symbol
|
|
499
|
+
unless VALID_ADAPTER_TYPES.include?(value)
|
|
500
|
+
raise ArgumentError, "Unknown memory adapter type: #{value.inspect}. " \
|
|
501
|
+
"Valid types are: #{VALID_ADAPTER_TYPES.map(&:inspect).join(", ")}"
|
|
502
|
+
end
|
|
503
|
+
when Memory::Adapters::Base
|
|
504
|
+
# Valid adapter instance
|
|
505
|
+
else
|
|
506
|
+
raise ArgumentError, "Memory adapter must be a Symbol (#{VALID_ADAPTER_TYPES.map(&:inspect).join(", ")}) " \
|
|
507
|
+
"or an instance of Memory::Adapters::Base, got: #{value.class}"
|
|
508
|
+
end
|
|
509
|
+
@config[:memory_adapter] = value
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# Enable associative memory
|
|
513
|
+
#
|
|
514
|
+
# When enabled, the agent naturally surfaces tangential memories
|
|
515
|
+
# in conversation, like a person who brings up related topics.
|
|
516
|
+
# Exploration cards get a distinct "YOU ALSO REMEMBER" section
|
|
517
|
+
# and guidance is injected into the system prompt.
|
|
518
|
+
#
|
|
519
|
+
# @param value [Boolean] Whether to enable associative memory
|
|
520
|
+
# @return [void]
|
|
521
|
+
def associative(value)
|
|
522
|
+
@config[:memory_associative] = value
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Return collected memory configuration
|
|
526
|
+
#
|
|
527
|
+
# @return [Hash] Memory config with `memory_` prefixed keys
|
|
528
|
+
def to_h
|
|
529
|
+
@config.dup
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
end
|