swarm_memory 2.1.5 → 2.1.6
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_memory/version.rb +1 -1
- metadata +5 -184
- data/lib/claude_swarm/base_executor.rb +0 -133
- data/lib/claude_swarm/claude_code_executor.rb +0 -349
- data/lib/claude_swarm/claude_mcp_server.rb +0 -78
- data/lib/claude_swarm/cli.rb +0 -697
- data/lib/claude_swarm/commands/ps.rb +0 -215
- data/lib/claude_swarm/commands/show.rb +0 -139
- data/lib/claude_swarm/configuration.rb +0 -373
- data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
- data/lib/claude_swarm/json_handler.rb +0 -91
- data/lib/claude_swarm/mcp_generator.rb +0 -230
- data/lib/claude_swarm/openai/chat_completion.rb +0 -256
- data/lib/claude_swarm/openai/executor.rb +0 -256
- data/lib/claude_swarm/openai/responses.rb +0 -319
- data/lib/claude_swarm/orchestrator.rb +0 -878
- data/lib/claude_swarm/process_tracker.rb +0 -78
- data/lib/claude_swarm/session_cost_calculator.rb +0 -209
- data/lib/claude_swarm/session_path.rb +0 -42
- data/lib/claude_swarm/settings_generator.rb +0 -77
- data/lib/claude_swarm/system_utils.rb +0 -46
- data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
- data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
- data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
- data/lib/claude_swarm/tools/task_tool.rb +0 -63
- data/lib/claude_swarm/version.rb +0 -5
- data/lib/claude_swarm/worktree_manager.rb +0 -475
- data/lib/claude_swarm/yaml_loader.rb +0 -22
- data/lib/claude_swarm.rb +0 -67
- data/lib/swarm_cli/cli.rb +0 -201
- data/lib/swarm_cli/command_registry.rb +0 -61
- data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
- data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
- data/lib/swarm_cli/commands/migrate.rb +0 -55
- data/lib/swarm_cli/commands/run.rb +0 -173
- data/lib/swarm_cli/config_loader.rb +0 -98
- data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
- data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
- data/lib/swarm_cli/interactive_repl.rb +0 -924
- data/lib/swarm_cli/mcp_serve_options.rb +0 -44
- data/lib/swarm_cli/mcp_tools_options.rb +0 -59
- data/lib/swarm_cli/migrate_options.rb +0 -54
- data/lib/swarm_cli/migrator.rb +0 -132
- data/lib/swarm_cli/options.rb +0 -151
- data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
- data/lib/swarm_cli/ui/components/content_block.rb +0 -120
- data/lib/swarm_cli/ui/components/divider.rb +0 -57
- data/lib/swarm_cli/ui/components/panel.rb +0 -62
- data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
- data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
- data/lib/swarm_cli/ui/formatters/number.rb +0 -58
- data/lib/swarm_cli/ui/formatters/text.rb +0 -77
- data/lib/swarm_cli/ui/formatters/time.rb +0 -73
- data/lib/swarm_cli/ui/icons.rb +0 -36
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
- data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
- data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
- data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
- data/lib/swarm_cli/version.rb +0 -5
- data/lib/swarm_cli.rb +0 -46
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
- data/lib/swarm_sdk/agent/builder.rb +0 -552
- data/lib/swarm_sdk/agent/chat.rb +0 -774
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
- 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 -78
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
- 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 -136
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
- data/lib/swarm_sdk/agent/context.rb +0 -116
- data/lib/swarm_sdk/agent/context_manager.rb +0 -315
- data/lib/swarm_sdk/agent/definition.rb +0 -477
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
- data/lib/swarm_sdk/builders/base_builder.rb +0 -409
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
- data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
- data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
- data/lib/swarm_sdk/concerns/validatable.rb +0 -55
- data/lib/swarm_sdk/configuration/parser.rb +0 -353
- data/lib/swarm_sdk/configuration/translator.rb +0 -255
- data/lib/swarm_sdk/configuration.rb +0 -135
- data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
- data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
- 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/defaults.rb +0 -196
- 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 -255
- 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 -1
- data/lib/swarm_sdk/models.rb +0 -120
- 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 -236
- 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 -117
- data/lib/swarm_sdk/restore_result.rb +0 -65
- data/lib/swarm_sdk/result.rb +0 -123
- 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 -683
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
- data/lib/swarm_sdk/swarm/builder.rb +0 -249
- data/lib/swarm_sdk/swarm/executor.rb +0 -213
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
- data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
- data/lib/swarm_sdk/swarm.rb +0 -717
- data/lib/swarm_sdk/swarm_loader.rb +0 -145
- data/lib/swarm_sdk/swarm_registry.rb +0 -136
- data/lib/swarm_sdk/tools/bash.rb +0 -282
- data/lib/swarm_sdk/tools/clock.rb +0 -44
- data/lib/swarm_sdk/tools/delegate.rb +0 -267
- 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 -163
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
- 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 -272
- 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 -98
- data/lib/swarm_sdk/tools/todo_write.rb +0 -235
- data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
- data/lib/swarm_sdk/tools/write.rb +0 -112
- 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 -79
- data/lib/swarm_sdk/workflow/builder.rb +0 -143
- data/lib/swarm_sdk/workflow/executor.rb +0 -497
- data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
- data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
- data/lib/swarm_sdk/workflow.rb +0 -554
- data/lib/swarm_sdk.rb +0 -524
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Agent
|
|
5
|
-
module ChatHelpers
|
|
6
|
-
# Manages context tracking, delegation tracking, and logging callbacks
|
|
7
|
-
#
|
|
8
|
-
# Responsibilities:
|
|
9
|
-
# - Register RubyLLM callbacks for logging
|
|
10
|
-
# - Track tool executions
|
|
11
|
-
# - Track delegations (which tool calls are delegations)
|
|
12
|
-
# - Emit log events via LogStream
|
|
13
|
-
# - Check context warnings
|
|
14
|
-
#
|
|
15
|
-
# This is a stateful helper that's instantiated per Agent::Chat instance.
|
|
16
|
-
#
|
|
17
|
-
# ## Thread Safety and Fiber-Local Storage
|
|
18
|
-
#
|
|
19
|
-
# IMPORTANT: LogStream.emit calls in this class DO NOT explicitly pass
|
|
20
|
-
# swarm_id, parent_swarm_id, or execution_id. These values are automatically
|
|
21
|
-
# injected from Fiber-local storage (Fiber[:swarm_id], etc.) by LogStream.emit.
|
|
22
|
-
#
|
|
23
|
-
# Why: In threaded environments (Puma, Sidekiq), swarm/agent instances may be
|
|
24
|
-
# reused across multiple requests/jobs. If we explicitly pass @agent_context.swarm_id,
|
|
25
|
-
# callbacks would use STALE values from the first request, causing events to be
|
|
26
|
-
# lost or misattributed.
|
|
27
|
-
#
|
|
28
|
-
# By relying on Fiber-local storage, each request/job gets the correct context
|
|
29
|
-
# even when reusing the same swarm instance. Fiber storage is set at the start
|
|
30
|
-
# of Swarm#execute and inherited by child fibers (tool calls, delegations).
|
|
31
|
-
#
|
|
32
|
-
# This design works correctly in both:
|
|
33
|
-
# - Single-threaded environments (rails runner, console)
|
|
34
|
-
# - Multi-threaded environments (Puma, Sidekiq)
|
|
35
|
-
class ContextTracker
|
|
36
|
-
include LoggingHelpers
|
|
37
|
-
|
|
38
|
-
attr_reader :agent_context
|
|
39
|
-
|
|
40
|
-
def initialize(chat, agent_context)
|
|
41
|
-
@chat = chat
|
|
42
|
-
@agent_context = agent_context
|
|
43
|
-
@tool_executions = []
|
|
44
|
-
@finish_reason_override = nil
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Set a custom finish reason for the next agent_stop event
|
|
48
|
-
#
|
|
49
|
-
# This is used when finish_agent or finish_swarm terminates execution early.
|
|
50
|
-
#
|
|
51
|
-
# @param reason [String] Custom finish reason (e.g., "finish_agent", "finish_swarm")
|
|
52
|
-
attr_writer :finish_reason_override
|
|
53
|
-
|
|
54
|
-
# Setup logging callbacks
|
|
55
|
-
#
|
|
56
|
-
# Registers RubyLLM callbacks to collect data and emit log events.
|
|
57
|
-
# Should only be called when LogStream.emitter is set.
|
|
58
|
-
#
|
|
59
|
-
# @return [void]
|
|
60
|
-
def setup_logging
|
|
61
|
-
register_logging_callbacks
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Extract agent name from delegation tool name
|
|
65
|
-
#
|
|
66
|
-
# Converts "#{Tools::Delegate::TOOL_NAME_PREFIX}[AgentName]" to "agent_name"
|
|
67
|
-
# Example: "WorkWithWorker" -> "worker"
|
|
68
|
-
#
|
|
69
|
-
# @param tool_name [String] Delegation tool name
|
|
70
|
-
# @return [String] Agent name
|
|
71
|
-
def extract_delegate_agent_name(tool_name)
|
|
72
|
-
# Remove tool name prefix and lowercase first letter
|
|
73
|
-
agent_name = tool_name.to_s.sub(/^#{Tools::Delegate::TOOL_NAME_PREFIX}/, "")
|
|
74
|
-
# Convert from PascalCase to lowercase (e.g., "Worker" -> "worker", "BackendDev" -> "backendDev")
|
|
75
|
-
agent_name[0] = agent_name[0].downcase unless agent_name.empty?
|
|
76
|
-
agent_name
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
private
|
|
80
|
-
|
|
81
|
-
# Extract usage information from an assistant message
|
|
82
|
-
#
|
|
83
|
-
# @param message [RubyLLM::Message] Assistant message with usage data
|
|
84
|
-
# @return [Hash] Usage information
|
|
85
|
-
def extract_usage_info(message)
|
|
86
|
-
cost_info = calculate_cost(message)
|
|
87
|
-
context_usage = if @chat.respond_to?(:cumulative_input_tokens)
|
|
88
|
-
{
|
|
89
|
-
cumulative_input_tokens: @chat.cumulative_input_tokens,
|
|
90
|
-
cumulative_output_tokens: @chat.cumulative_output_tokens,
|
|
91
|
-
cumulative_total_tokens: @chat.cumulative_total_tokens,
|
|
92
|
-
cumulative_cached_tokens: @chat.cumulative_cached_tokens,
|
|
93
|
-
cumulative_cache_creation_tokens: @chat.cumulative_cache_creation_tokens,
|
|
94
|
-
effective_input_tokens: @chat.effective_input_tokens,
|
|
95
|
-
context_limit: @chat.context_limit,
|
|
96
|
-
tokens_used_percentage: "#{@chat.context_usage_percentage}%",
|
|
97
|
-
tokens_remaining: @chat.tokens_remaining,
|
|
98
|
-
}
|
|
99
|
-
else
|
|
100
|
-
{}
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
input_tokens: message.input_tokens,
|
|
105
|
-
output_tokens: message.output_tokens,
|
|
106
|
-
cached_tokens: message.cached_tokens,
|
|
107
|
-
cache_creation_tokens: message.cache_creation_tokens,
|
|
108
|
-
total_tokens: (message.input_tokens || 0) + (message.output_tokens || 0),
|
|
109
|
-
input_cost: cost_info[:input_cost],
|
|
110
|
-
output_cost: cost_info[:output_cost],
|
|
111
|
-
total_cost: cost_info[:total_cost],
|
|
112
|
-
}.merge(context_usage)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Register RubyLLM chat callbacks to collect data and trigger logging
|
|
116
|
-
#
|
|
117
|
-
# This sets up low-level RubyLLM callbacks for technical plumbing (tracking state,
|
|
118
|
-
# collecting tool results), then emits log events via LogStream.
|
|
119
|
-
#
|
|
120
|
-
# @return [void]
|
|
121
|
-
def register_logging_callbacks
|
|
122
|
-
# Collect tool execution results (technical plumbing)
|
|
123
|
-
@chat.on_tool_result do |result|
|
|
124
|
-
@tool_executions << {
|
|
125
|
-
result: serialize_result(result),
|
|
126
|
-
completed_at: Time.now.utc.iso8601,
|
|
127
|
-
}
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Track delegations and emit agent_step/agent_stop events
|
|
131
|
-
@chat.on_end_message do |message|
|
|
132
|
-
next unless message
|
|
133
|
-
|
|
134
|
-
case message.role
|
|
135
|
-
when :assistant
|
|
136
|
-
if message.tool_call?
|
|
137
|
-
# Assistant made tool calls - emit agent_step event
|
|
138
|
-
trigger_agent_step(message, tool_executions: @tool_executions) if @chat.hook_executor
|
|
139
|
-
@tool_executions.clear
|
|
140
|
-
elsif @chat.hook_executor
|
|
141
|
-
# Final response (finish_reason: "stop") - fire agent_stop
|
|
142
|
-
trigger_agent_stop(message, tool_executions: @tool_executions)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Check context warnings after each assistant message
|
|
146
|
-
# Uses unified implementation in HookIntegration
|
|
147
|
-
@chat.check_context_warnings if @chat.respond_to?(:check_context_warnings)
|
|
148
|
-
when :tool
|
|
149
|
-
# Handle delegation tracking and logging (technical plumbing)
|
|
150
|
-
if @agent_context.delegation?(call_id: message.tool_call_id)
|
|
151
|
-
delegate_from = @agent_context.delegation_target(call_id: message.tool_call_id)
|
|
152
|
-
|
|
153
|
-
# Emit delegation result log event
|
|
154
|
-
LogStream.emit(
|
|
155
|
-
type: "delegation_result",
|
|
156
|
-
agent: @agent_context.name,
|
|
157
|
-
delegate_from: delegate_from,
|
|
158
|
-
tool_call_id: message.tool_call_id,
|
|
159
|
-
result: serialize_result(message.content),
|
|
160
|
-
metadata: @agent_context.metadata,
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
@agent_context.clear_delegation(call_id: message.tool_call_id)
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# Track delegations when tool calls are made
|
|
169
|
-
@chat.on_tool_call do |tool_call|
|
|
170
|
-
if @agent_context.delegation_tool?(tool_call.name)
|
|
171
|
-
# Extract agent name from tool name (DelegateTaskTo[AgentName] -> agent_name)
|
|
172
|
-
agent_name = extract_delegate_agent_name(tool_call.name)
|
|
173
|
-
|
|
174
|
-
@agent_context.track_delegation(call_id: tool_call.id, target: agent_name)
|
|
175
|
-
|
|
176
|
-
# Emit delegation log event
|
|
177
|
-
LogStream.emit(
|
|
178
|
-
type: "agent_delegation",
|
|
179
|
-
agent: @agent_context.name,
|
|
180
|
-
tool_call_id: tool_call.id,
|
|
181
|
-
delegate_to: agent_name,
|
|
182
|
-
arguments: tool_call.arguments,
|
|
183
|
-
metadata: @agent_context.metadata,
|
|
184
|
-
)
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# Trigger agent_step callback
|
|
190
|
-
#
|
|
191
|
-
# This fires when the agent makes an intermediate response with tool calls.
|
|
192
|
-
# The agent hasn't finished yet - it's requesting tools to continue processing.
|
|
193
|
-
#
|
|
194
|
-
# @param message [RubyLLM::Message] Assistant message with tool calls
|
|
195
|
-
# @param tool_executions [Array<Hash>] Tool execution results (should be empty for steps)
|
|
196
|
-
# @return [void]
|
|
197
|
-
def trigger_agent_step(message, tool_executions: [])
|
|
198
|
-
return unless @chat.hook_executor
|
|
199
|
-
|
|
200
|
-
usage_info = extract_usage_info(message)
|
|
201
|
-
|
|
202
|
-
context = Hooks::Context.new(
|
|
203
|
-
event: :agent_step,
|
|
204
|
-
agent_name: @agent_context.name,
|
|
205
|
-
swarm: @chat.hook_swarm,
|
|
206
|
-
metadata: {
|
|
207
|
-
model: message.model_id,
|
|
208
|
-
content: message.content,
|
|
209
|
-
tool_calls: format_tool_calls(message.tool_calls),
|
|
210
|
-
finish_reason: "tool_calls",
|
|
211
|
-
usage: usage_info,
|
|
212
|
-
tool_executions: tool_executions.empty? ? nil : tool_executions,
|
|
213
|
-
timestamp: Time.now.utc.iso8601,
|
|
214
|
-
},
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
agent_hooks = @chat.hook_agent_hooks[:agent_step] || []
|
|
218
|
-
|
|
219
|
-
@chat.hook_executor.execute_safe(
|
|
220
|
-
event: :agent_step,
|
|
221
|
-
context: context,
|
|
222
|
-
callbacks: agent_hooks,
|
|
223
|
-
)
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
# Trigger agent_stop callback
|
|
227
|
-
#
|
|
228
|
-
# This fires when the agent completes with a final response (no more tool calls).
|
|
229
|
-
#
|
|
230
|
-
# @param message [RubyLLM::Message] Assistant message with final content
|
|
231
|
-
# @param tool_executions [Array<Hash>] Tool execution results (if any)
|
|
232
|
-
# @return [void]
|
|
233
|
-
def trigger_agent_stop(message, tool_executions: [])
|
|
234
|
-
return unless @chat.hook_executor
|
|
235
|
-
|
|
236
|
-
usage_info = extract_usage_info(message)
|
|
237
|
-
|
|
238
|
-
# Use override if set (e.g., "finish_agent"), otherwise default to "stop"
|
|
239
|
-
finish_reason = @finish_reason_override || "stop"
|
|
240
|
-
@finish_reason_override = nil # Clear after use
|
|
241
|
-
|
|
242
|
-
context = Hooks::Context.new(
|
|
243
|
-
event: :agent_stop,
|
|
244
|
-
agent_name: @agent_context.name,
|
|
245
|
-
swarm: @chat.hook_swarm,
|
|
246
|
-
metadata: {
|
|
247
|
-
model: message.model_id,
|
|
248
|
-
content: message.content,
|
|
249
|
-
tool_calls: nil, # Final response has no tool calls
|
|
250
|
-
finish_reason: finish_reason,
|
|
251
|
-
usage: usage_info,
|
|
252
|
-
tool_executions: tool_executions.empty? ? nil : tool_executions,
|
|
253
|
-
timestamp: Time.now.utc.iso8601,
|
|
254
|
-
},
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
agent_hooks = @chat.hook_agent_hooks[:agent_stop] || []
|
|
258
|
-
|
|
259
|
-
@chat.hook_executor.execute_safe(
|
|
260
|
-
event: :agent_stop,
|
|
261
|
-
context: context,
|
|
262
|
-
callbacks: agent_hooks,
|
|
263
|
-
)
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
end
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "monitor"
|
|
4
|
-
|
|
5
|
-
module SwarmSDK
|
|
6
|
-
module Agent
|
|
7
|
-
module ChatHelpers
|
|
8
|
-
# Minimal event emitter that mirrors RubyLLM::Chat's callback pattern
|
|
9
|
-
#
|
|
10
|
-
# Provides multi-subscriber support for events like tool_call, tool_result,
|
|
11
|
-
# new_message, end_message. This is thread-safe and supports unsubscription.
|
|
12
|
-
module EventEmitter
|
|
13
|
-
# Represents an active subscription to a callback event.
|
|
14
|
-
# Returned by {#subscribe} and can be used to unsubscribe later.
|
|
15
|
-
class Subscription
|
|
16
|
-
attr_reader :tag
|
|
17
|
-
|
|
18
|
-
def initialize(callback_list, callback, monitor:, tag: nil)
|
|
19
|
-
@callback_list = callback_list
|
|
20
|
-
@callback = callback
|
|
21
|
-
@monitor = monitor
|
|
22
|
-
@tag = tag
|
|
23
|
-
@active = true
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Removes this subscription from the callback list.
|
|
27
|
-
# @return [Boolean] true if successfully unsubscribed, false if already inactive
|
|
28
|
-
def unsubscribe # rubocop:disable Naming/PredicateMethod
|
|
29
|
-
@monitor.synchronize do
|
|
30
|
-
return false unless @active
|
|
31
|
-
|
|
32
|
-
@callback_list.delete(@callback)
|
|
33
|
-
@active = false
|
|
34
|
-
end
|
|
35
|
-
true
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Checks if this subscription is still active.
|
|
39
|
-
# @return [Boolean] true if still subscribed
|
|
40
|
-
def active?
|
|
41
|
-
@monitor.synchronize do
|
|
42
|
-
@active && @callback_list.include?(@callback)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def inspect
|
|
47
|
-
"#<#{self.class.name} tag=#{@tag.inspect} active=#{active?}>"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Initialize the event emitter system
|
|
52
|
-
#
|
|
53
|
-
# Sets up @callbacks hash and @callback_monitor for thread safety.
|
|
54
|
-
# Must be called in Chat#initialize.
|
|
55
|
-
#
|
|
56
|
-
# @return [void]
|
|
57
|
-
def initialize_event_emitter
|
|
58
|
-
@callbacks = {
|
|
59
|
-
new_message: [],
|
|
60
|
-
end_message: [],
|
|
61
|
-
tool_call: [],
|
|
62
|
-
tool_result: [],
|
|
63
|
-
}
|
|
64
|
-
@callback_monitor = Monitor.new
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Subscribes to an event with the given block.
|
|
68
|
-
# Returns a {Subscription} that can be used to unsubscribe.
|
|
69
|
-
#
|
|
70
|
-
# @param event [Symbol] The event to subscribe to
|
|
71
|
-
# @param tag [String, nil] Optional tag for debugging/identification
|
|
72
|
-
# @yield The block to call when the event fires
|
|
73
|
-
# @return [Subscription] An object that can be used to unsubscribe
|
|
74
|
-
# @raise [ArgumentError] if event is not recognized
|
|
75
|
-
def subscribe(event, tag: nil, &block)
|
|
76
|
-
@callback_monitor.synchronize do
|
|
77
|
-
unless @callbacks.key?(event)
|
|
78
|
-
raise ArgumentError, "Unknown event: #{event}. Valid events: #{@callbacks.keys.join(", ")}"
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
@callbacks[event] << block
|
|
82
|
-
Subscription.new(@callbacks[event], block, monitor: @callback_monitor, tag: tag)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Subscribes to an event that automatically unsubscribes after firing once.
|
|
87
|
-
#
|
|
88
|
-
# @param event [Symbol] The event to subscribe to
|
|
89
|
-
# @param tag [String, nil] Optional tag for debugging/identification
|
|
90
|
-
# @yield The block to call when the event fires (once)
|
|
91
|
-
# @return [Subscription] An object that can be used to unsubscribe before it fires
|
|
92
|
-
def once(event, tag: nil, &block)
|
|
93
|
-
subscription = nil
|
|
94
|
-
wrapper = lambda do |*args|
|
|
95
|
-
subscription&.unsubscribe
|
|
96
|
-
block.call(*args)
|
|
97
|
-
end
|
|
98
|
-
subscription = subscribe(event, tag: tag, &wrapper)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Registers a callback for when a new message starts being generated.
|
|
102
|
-
# Multiple callbacks can be registered and all will fire in registration order.
|
|
103
|
-
#
|
|
104
|
-
# @yield Block called when a new message starts
|
|
105
|
-
# @return [self] for chaining
|
|
106
|
-
def on_new_message(&block)
|
|
107
|
-
subscribe(:new_message, &block)
|
|
108
|
-
self
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Registers a callback for when a message is complete.
|
|
112
|
-
# Multiple callbacks can be registered and all will fire in registration order.
|
|
113
|
-
#
|
|
114
|
-
# @yield [Message] Block called with the completed message
|
|
115
|
-
# @return [self] for chaining
|
|
116
|
-
def on_end_message(&block)
|
|
117
|
-
subscribe(:end_message, &block)
|
|
118
|
-
self
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Registers a callback for when a tool is called.
|
|
122
|
-
# Multiple callbacks can be registered and all will fire in registration order.
|
|
123
|
-
#
|
|
124
|
-
# @yield [ToolCall] Block called with the tool call object
|
|
125
|
-
# @return [self] for chaining
|
|
126
|
-
def on_tool_call(&block)
|
|
127
|
-
subscribe(:tool_call, &block)
|
|
128
|
-
self
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Registers a callback for when a tool returns a result.
|
|
132
|
-
# Multiple callbacks can be registered and all will fire in registration order.
|
|
133
|
-
#
|
|
134
|
-
# @yield [Object] Block called with the tool result
|
|
135
|
-
# @return [self] for chaining
|
|
136
|
-
def on_tool_result(&block)
|
|
137
|
-
subscribe(:tool_result, &block)
|
|
138
|
-
self
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Clears all callbacks for the specified event, or all events if none specified.
|
|
142
|
-
#
|
|
143
|
-
# @param event [Symbol, nil] The event to clear callbacks for, or nil for all events
|
|
144
|
-
# @return [self] for chaining
|
|
145
|
-
def clear_callbacks(event = nil)
|
|
146
|
-
@callback_monitor.synchronize do
|
|
147
|
-
if event
|
|
148
|
-
@callbacks[event]&.clear
|
|
149
|
-
else
|
|
150
|
-
@callbacks.each_value(&:clear)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
self
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Returns the number of callbacks registered for the specified event.
|
|
157
|
-
#
|
|
158
|
-
# @param event [Symbol, nil] The event to count callbacks for, or nil for all events
|
|
159
|
-
# @return [Integer, Hash] Count for specific event, or hash of counts for all events
|
|
160
|
-
def callback_count(event = nil)
|
|
161
|
-
@callback_monitor.synchronize do
|
|
162
|
-
if event
|
|
163
|
-
@callbacks[event]&.size || 0
|
|
164
|
-
else
|
|
165
|
-
@callbacks.transform_values(&:size)
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
# Emits an event to all registered subscribers.
|
|
173
|
-
# Callbacks are executed in registration order (FIFO).
|
|
174
|
-
# Errors in callbacks are isolated - one failing callback doesn't prevent others from running.
|
|
175
|
-
#
|
|
176
|
-
# @param event [Symbol] The event to emit
|
|
177
|
-
# @param args [Array] Arguments to pass to each callback
|
|
178
|
-
# @return [void]
|
|
179
|
-
def emit(event, *args)
|
|
180
|
-
# Snapshot callbacks under lock (fast operation)
|
|
181
|
-
callbacks = @callback_monitor.synchronize { @callbacks[event]&.dup || [] }
|
|
182
|
-
|
|
183
|
-
# Execute callbacks outside lock (safe, non-blocking)
|
|
184
|
-
callbacks.each do |callback|
|
|
185
|
-
callback.call(*args)
|
|
186
|
-
rescue StandardError => e
|
|
187
|
-
handle_callback_error(event, callback, e)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Hook for custom error handling when a callback raises an exception.
|
|
192
|
-
# Override this method in Chat to customize error behavior.
|
|
193
|
-
#
|
|
194
|
-
# @param event [Symbol] The event that was being emitted
|
|
195
|
-
# @param callback [Proc] The callback that raised the error
|
|
196
|
-
# @param error [StandardError] The error that was raised
|
|
197
|
-
# @return [void]
|
|
198
|
-
def handle_callback_error(event, _callback, error)
|
|
199
|
-
warn("[SwarmSDK] Callback error in #{event}: #{error.class} - #{error.message}")
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|