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.
- 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/document_converters/base.rb +84 -0
- data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
- data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
- data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -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 +213 -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 +88 -149
- 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
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Hooks
|
|
5
|
-
# Represents a tool call in the hooks system
|
|
6
|
-
#
|
|
7
|
-
# This is a simple value object that wraps tool call information
|
|
8
|
-
# from RubyLLM in a consistent, immutable format for hooks.
|
|
9
|
-
#
|
|
10
|
-
# @example Access tool call information in a hook
|
|
11
|
-
# swarm.add_callback(:pre_tool_use) do |context|
|
|
12
|
-
# puts "Tool: #{context.tool_call.name}"
|
|
13
|
-
# puts "Parameters: #{context.tool_call.parameters.inspect}"
|
|
14
|
-
# end
|
|
15
|
-
class ToolCall
|
|
16
|
-
attr_reader :id, :name, :parameters
|
|
17
|
-
|
|
18
|
-
# @param id [String] Unique identifier for this tool call
|
|
19
|
-
# @param name [String] Name of the tool being called
|
|
20
|
-
# @param parameters [Hash] Parameters passed to the tool
|
|
21
|
-
def initialize(id:, name:, parameters:)
|
|
22
|
-
@id = id
|
|
23
|
-
@name = name
|
|
24
|
-
@parameters = parameters
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Convert to hash representation
|
|
28
|
-
#
|
|
29
|
-
# @return [Hash] Hash with id, name, and parameters
|
|
30
|
-
def to_h
|
|
31
|
-
{ id: @id, name: @name, parameters: @parameters }
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Hooks
|
|
5
|
-
# Represents the result of a tool execution
|
|
6
|
-
#
|
|
7
|
-
# This is a simple value object that wraps tool execution results
|
|
8
|
-
# in a consistent format for post-tool-use hooks.
|
|
9
|
-
#
|
|
10
|
-
# @example Access tool result in a hook
|
|
11
|
-
# swarm.add_callback(:post_tool_use) do |context|
|
|
12
|
-
# if context.tool_result.success?
|
|
13
|
-
# puts "Tool succeeded: #{context.tool_result.content}"
|
|
14
|
-
# else
|
|
15
|
-
# puts "Tool failed: #{context.tool_result.error}"
|
|
16
|
-
# end
|
|
17
|
-
# end
|
|
18
|
-
class ToolResult
|
|
19
|
-
attr_reader :tool_call_id, :tool_name, :content, :success, :error
|
|
20
|
-
|
|
21
|
-
# @param tool_call_id [String] ID of the tool call this result corresponds to
|
|
22
|
-
# @param tool_name [String] Name of the tool that was executed
|
|
23
|
-
# @param content [String, nil] Result content (if successful)
|
|
24
|
-
# @param success [Boolean] Whether the tool execution succeeded
|
|
25
|
-
# @param error [String, nil] Error message (if failed)
|
|
26
|
-
def initialize(tool_call_id:, tool_name:, content: nil, success: true, error: nil)
|
|
27
|
-
@tool_call_id = tool_call_id
|
|
28
|
-
@tool_name = tool_name
|
|
29
|
-
@content = content
|
|
30
|
-
@success = success
|
|
31
|
-
@error = error
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Check if the tool execution succeeded
|
|
35
|
-
#
|
|
36
|
-
# @return [Boolean] true if successful
|
|
37
|
-
def success?
|
|
38
|
-
@success
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Check if the tool execution failed
|
|
42
|
-
#
|
|
43
|
-
# @return [Boolean] true if failed
|
|
44
|
-
def failure?
|
|
45
|
-
!@success
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Convert to hash representation
|
|
49
|
-
#
|
|
50
|
-
# @return [Hash] Hash with all result attributes
|
|
51
|
-
def to_h
|
|
52
|
-
{
|
|
53
|
-
tool_call_id: @tool_call_id,
|
|
54
|
-
tool_name: @tool_name,
|
|
55
|
-
content: @content,
|
|
56
|
-
success: @success,
|
|
57
|
-
error: @error,
|
|
58
|
-
}
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
# LogCollector manages subscriber callbacks for log events with filtering support.
|
|
5
|
-
#
|
|
6
|
-
# This module acts as an emitter implementation that forwards events
|
|
7
|
-
# to user-registered callbacks. It's designed to be set as the LogStream
|
|
8
|
-
# emitter during swarm execution.
|
|
9
|
-
#
|
|
10
|
-
# ## Features
|
|
11
|
-
#
|
|
12
|
-
# - **Filtered Subscriptions**: Subscribe to specific agents, event types, or swarm IDs
|
|
13
|
-
# - **Unsubscribe Support**: Remove subscriptions by ID to prevent memory leaks
|
|
14
|
-
# - **Error Isolation**: One subscriber's error doesn't break others
|
|
15
|
-
# - **Thread Safety**: Fiber-local storage for multi-threaded environments
|
|
16
|
-
#
|
|
17
|
-
# ## Thread Safety for Multi-Threaded Environments (Puma, Sidekiq)
|
|
18
|
-
#
|
|
19
|
-
# Subscriptions are stored in Fiber-local storage (Fiber[:log_subscriptions]) instead
|
|
20
|
-
# of class instance variables. This ensures callbacks registered in the parent
|
|
21
|
-
# thread/fiber are accessible to child fibers created by Async reactor.
|
|
22
|
-
#
|
|
23
|
-
# Why: In Puma/Sidekiq, class instance variables (@subscriptions) are thread-isolated
|
|
24
|
-
# and don't properly propagate to child fibers. Using Fiber-local storage ensures
|
|
25
|
-
# events emitted from within Async blocks can reach registered subscriptions.
|
|
26
|
-
#
|
|
27
|
-
# Child fibers inherit parent fiber-local storage automatically, so events
|
|
28
|
-
# emitted from agent callbacks (on_tool_call, on_end_message, etc.) executing
|
|
29
|
-
# in child fibers can still reach the parent's registered subscriptions.
|
|
30
|
-
#
|
|
31
|
-
# ## Usage
|
|
32
|
-
#
|
|
33
|
-
# # Subscribe to all events
|
|
34
|
-
# sub_id = LogCollector.subscribe { |event| puts event }
|
|
35
|
-
#
|
|
36
|
-
# # Subscribe to specific agent
|
|
37
|
-
# sub_id = LogCollector.subscribe(filter: { agent: :backend }) { |event|
|
|
38
|
-
# puts "Backend: #{event}"
|
|
39
|
-
# }
|
|
40
|
-
#
|
|
41
|
-
# # Subscribe to specific event types
|
|
42
|
-
# sub_id = LogCollector.subscribe(filter: { type: ["tool_call", "tool_result"] }) { |event|
|
|
43
|
-
# log_tool_activity(event)
|
|
44
|
-
# }
|
|
45
|
-
#
|
|
46
|
-
# # Subscribe with regex matching
|
|
47
|
-
# sub_id = LogCollector.subscribe(filter: { type: /^tool_/ }) { |event|
|
|
48
|
-
# track_tool_usage(event)
|
|
49
|
-
# }
|
|
50
|
-
#
|
|
51
|
-
# # Unsubscribe when done
|
|
52
|
-
# LogCollector.unsubscribe(sub_id)
|
|
53
|
-
#
|
|
54
|
-
# # After execution, reset for next use
|
|
55
|
-
# LogCollector.reset!
|
|
56
|
-
#
|
|
57
|
-
module LogCollector
|
|
58
|
-
# Subscription object with filtering capabilities
|
|
59
|
-
#
|
|
60
|
-
# Encapsulates a callback with optional filters. Filters can match
|
|
61
|
-
# against agent name, event type, swarm_id, or any event field.
|
|
62
|
-
class Subscription
|
|
63
|
-
attr_reader :id, :filter, :callback
|
|
64
|
-
|
|
65
|
-
# Initialize a subscription
|
|
66
|
-
#
|
|
67
|
-
# @param filter [Hash] Filter criteria
|
|
68
|
-
# @param callback [Proc] Block to call when event matches
|
|
69
|
-
def initialize(filter: {}, &callback)
|
|
70
|
-
@id = SecureRandom.uuid
|
|
71
|
-
@filter = normalize_filter(filter)
|
|
72
|
-
@callback = callback
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Check if event matches filter criteria
|
|
76
|
-
#
|
|
77
|
-
# Empty filter matches all events. Multiple filter keys are AND'd together.
|
|
78
|
-
#
|
|
79
|
-
# @param event [Hash] Event entry with :type, :agent, etc.
|
|
80
|
-
# @return [Boolean] True if event matches filter
|
|
81
|
-
def matches?(event)
|
|
82
|
-
return true if @filter.empty?
|
|
83
|
-
|
|
84
|
-
@filter.all? do |key, matcher|
|
|
85
|
-
value = event[key]
|
|
86
|
-
case matcher
|
|
87
|
-
when Array
|
|
88
|
-
# Match if value is in array (handles symbols/strings)
|
|
89
|
-
matcher.include?(value) || matcher.map(&:to_s).include?(value.to_s)
|
|
90
|
-
when Regexp
|
|
91
|
-
# Regex matching
|
|
92
|
-
value.to_s.match?(matcher)
|
|
93
|
-
when Proc
|
|
94
|
-
# Custom matcher
|
|
95
|
-
matcher.call(value)
|
|
96
|
-
else
|
|
97
|
-
# Exact match (handles symbols/strings)
|
|
98
|
-
matcher == value || matcher.to_s == value.to_s
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
private
|
|
104
|
-
|
|
105
|
-
# Normalize filter keys to symbols for consistent matching
|
|
106
|
-
#
|
|
107
|
-
# @param filter [Hash] Raw filter
|
|
108
|
-
# @return [Hash] Normalized filter with symbol keys
|
|
109
|
-
def normalize_filter(filter)
|
|
110
|
-
filter.transform_keys(&:to_sym)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
class << self
|
|
115
|
-
# Subscribe to log events with optional filtering
|
|
116
|
-
#
|
|
117
|
-
# Registers a callback that will receive events matching the filter criteria.
|
|
118
|
-
# Returns a subscription ID that can be used to unsubscribe later.
|
|
119
|
-
#
|
|
120
|
-
# @param filter [Hash] Filter criteria (empty = all events)
|
|
121
|
-
# - :agent [Symbol, String, Array, Regexp] Agent name(s) to observe
|
|
122
|
-
# - :type [String, Array, Regexp] Event type(s) to receive
|
|
123
|
-
# - :swarm_id [String] Specific swarm instance
|
|
124
|
-
# - Any other key matches against event fields
|
|
125
|
-
# @yield [Hash] Log event entry
|
|
126
|
-
# @return [String] Subscription ID for unsubscribe
|
|
127
|
-
#
|
|
128
|
-
# @example Subscribe to all events
|
|
129
|
-
# LogCollector.subscribe { |event| puts event }
|
|
130
|
-
#
|
|
131
|
-
# @example Subscribe to specific agent's tool calls
|
|
132
|
-
# sub_id = LogCollector.subscribe(
|
|
133
|
-
# filter: { agent: :backend, type: /^tool_/ }
|
|
134
|
-
# ) do |event|
|
|
135
|
-
# puts "Backend used tool: #{event[:tool]}"
|
|
136
|
-
# end
|
|
137
|
-
#
|
|
138
|
-
# @example Subscribe to multiple agents
|
|
139
|
-
# LogCollector.subscribe(
|
|
140
|
-
# filter: { agent: [:backend, :frontend], type: "agent_stop" }
|
|
141
|
-
# ) { |e| record_completion(e) }
|
|
142
|
-
def subscribe(filter: {}, &block)
|
|
143
|
-
subscription = Subscription.new(filter: filter, &block)
|
|
144
|
-
subscriptions << subscription
|
|
145
|
-
subscription.id
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Unsubscribe by ID
|
|
149
|
-
#
|
|
150
|
-
# Removes a subscription to prevent memory leaks and stop receiving events.
|
|
151
|
-
#
|
|
152
|
-
# @param subscription_id [String] ID returned from subscribe
|
|
153
|
-
# @return [Subscription, nil] Removed subscription or nil if not found
|
|
154
|
-
def unsubscribe(subscription_id)
|
|
155
|
-
index = subscriptions.find_index { |s| s.id == subscription_id }
|
|
156
|
-
return unless index
|
|
157
|
-
|
|
158
|
-
subscriptions.delete_at(index)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Clear all subscriptions
|
|
162
|
-
#
|
|
163
|
-
# Removes all subscriptions. Useful for testing or execution cleanup.
|
|
164
|
-
#
|
|
165
|
-
# @return [void]
|
|
166
|
-
def clear_subscriptions
|
|
167
|
-
subscriptions.clear
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Get current subscription count
|
|
171
|
-
#
|
|
172
|
-
# @return [Integer] Number of active subscriptions
|
|
173
|
-
def subscription_count
|
|
174
|
-
subscriptions.size
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Emit an event to all matching subscribers
|
|
178
|
-
#
|
|
179
|
-
# Automatically adds a timestamp if one doesn't exist.
|
|
180
|
-
# Errors in individual subscribers are isolated - one bad subscriber
|
|
181
|
-
# won't prevent others from receiving events.
|
|
182
|
-
#
|
|
183
|
-
# @param entry [Hash] Log event entry
|
|
184
|
-
# @return [void]
|
|
185
|
-
def emit(entry)
|
|
186
|
-
entry_with_timestamp = ensure_timestamp(entry)
|
|
187
|
-
|
|
188
|
-
subscriptions.each do |subscription|
|
|
189
|
-
next unless subscription.matches?(entry_with_timestamp)
|
|
190
|
-
|
|
191
|
-
begin
|
|
192
|
-
subscription.callback.call(entry_with_timestamp)
|
|
193
|
-
rescue StandardError => e
|
|
194
|
-
# Error isolation - don't let one subscriber break others
|
|
195
|
-
RubyLLM.logger.error("SwarmSDK: Subscription #{subscription.id} error: #{e.message}")
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# Reset the collector (clears subscriptions for next execution)
|
|
201
|
-
#
|
|
202
|
-
# @return [void]
|
|
203
|
-
def reset!
|
|
204
|
-
Fiber[:log_subscriptions] = []
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
private
|
|
208
|
-
|
|
209
|
-
# Get subscriptions from Fiber-local storage
|
|
210
|
-
#
|
|
211
|
-
# @return [Array<Subscription>] Current subscriptions
|
|
212
|
-
def subscriptions
|
|
213
|
-
Fiber[:log_subscriptions] ||= []
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
# Ensure event has a timestamp
|
|
217
|
-
#
|
|
218
|
-
# @param entry [Hash] Event entry
|
|
219
|
-
# @return [Hash] Entry with timestamp
|
|
220
|
-
def ensure_timestamp(entry)
|
|
221
|
-
return entry if entry.key?(:timestamp)
|
|
222
|
-
|
|
223
|
-
entry.merge(timestamp: Time.now.utc.iso8601(6))
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
data/lib/swarm_sdk/log_stream.rb
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
# LogStream provides a module-level singleton for emitting log events.
|
|
5
|
-
#
|
|
6
|
-
# This allows any component (tools, providers, agents) to emit structured
|
|
7
|
-
# log events without needing references to logger instances.
|
|
8
|
-
#
|
|
9
|
-
# ## Usage
|
|
10
|
-
#
|
|
11
|
-
# # Emit an event from anywhere in the SDK
|
|
12
|
-
# LogStream.emit(
|
|
13
|
-
# type: "user_prompt",
|
|
14
|
-
# agent: :backend,
|
|
15
|
-
# model: "claude-sonnet-4",
|
|
16
|
-
# message_count: 5
|
|
17
|
-
# )
|
|
18
|
-
#
|
|
19
|
-
# ## Thread Safety
|
|
20
|
-
#
|
|
21
|
-
# LogStream is thread-safe and fiber-safe:
|
|
22
|
-
# - Uses Fiber storage for per-request isolation in multi-threaded servers (Puma, Sidekiq)
|
|
23
|
-
# - Each thread/request has its own emitter instance
|
|
24
|
-
# - Child fibers inherit the emitter from their parent fiber
|
|
25
|
-
# - No cross-thread contamination of log events
|
|
26
|
-
#
|
|
27
|
-
# Usage pattern:
|
|
28
|
-
# 1. Set emitter BEFORE starting Async execution
|
|
29
|
-
# 2. During Async execution, only emit() (reads emitter)
|
|
30
|
-
# 3. Each event includes agent context for identification
|
|
31
|
-
#
|
|
32
|
-
# ## Testing
|
|
33
|
-
#
|
|
34
|
-
# # Inject a test emitter
|
|
35
|
-
# LogStream.emitter = TestEmitter.new
|
|
36
|
-
# # ... run tests ...
|
|
37
|
-
# LogStream.reset!
|
|
38
|
-
#
|
|
39
|
-
module LogStream
|
|
40
|
-
class << self
|
|
41
|
-
# Emit a log event
|
|
42
|
-
#
|
|
43
|
-
# Adds timestamp and forwards to the registered emitter.
|
|
44
|
-
# Auto-injects execution_id, swarm_id, and parent_swarm_id from Fiber storage.
|
|
45
|
-
# Explicit values in data override auto-injected ones.
|
|
46
|
-
#
|
|
47
|
-
# @param data [Hash] Event data (type, agent, and event-specific fields)
|
|
48
|
-
# @return [void]
|
|
49
|
-
def emit(**data)
|
|
50
|
-
emitter = Fiber[:log_stream_emitter]
|
|
51
|
-
return unless emitter
|
|
52
|
-
|
|
53
|
-
# Auto-inject execution context from Fiber storage
|
|
54
|
-
# Explicit values in data override auto-injected ones
|
|
55
|
-
auto_injected = {
|
|
56
|
-
execution_id: Fiber[:execution_id],
|
|
57
|
-
swarm_id: Fiber[:swarm_id],
|
|
58
|
-
parent_swarm_id: Fiber[:parent_swarm_id],
|
|
59
|
-
}.compact
|
|
60
|
-
|
|
61
|
-
entry = auto_injected.merge(data).merge(timestamp: Time.now.utc.iso8601(6)).compact
|
|
62
|
-
|
|
63
|
-
emitter.emit(entry)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Set the emitter (for dependency injection in tests)
|
|
67
|
-
#
|
|
68
|
-
# Stores emitter in Fiber storage for thread-safe, per-request isolation.
|
|
69
|
-
#
|
|
70
|
-
# @param emitter [#emit] Object responding to emit(Hash)
|
|
71
|
-
# @return [void]
|
|
72
|
-
def emitter=(emitter)
|
|
73
|
-
Fiber[:log_stream_emitter] = emitter
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Get the current emitter
|
|
77
|
-
#
|
|
78
|
-
# @return [#emit, nil] Current emitter or nil if not set
|
|
79
|
-
def emitter
|
|
80
|
-
Fiber[:log_stream_emitter]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Reset the emitter (for test cleanup)
|
|
84
|
-
#
|
|
85
|
-
# @return [void]
|
|
86
|
-
def reset!
|
|
87
|
-
Fiber[:log_stream_emitter] = nil
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Check if logging is enabled
|
|
91
|
-
#
|
|
92
|
-
# @return [Boolean] true if an emitter is configured
|
|
93
|
-
def enabled?
|
|
94
|
-
!Fiber[:log_stream_emitter].nil?
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Emit an internal error event
|
|
98
|
-
#
|
|
99
|
-
# Provides consistent error event emission for all internal errors.
|
|
100
|
-
# These are errors that occur during execution but are handled gracefully
|
|
101
|
-
# (with fallback behavior) rather than causing failures.
|
|
102
|
-
#
|
|
103
|
-
# @param error [Exception] The caught exception
|
|
104
|
-
# @param source [String] Source module/class (e.g., "hook_triggers", "context_compactor")
|
|
105
|
-
# @param context [String] Specific operation context (e.g., "swarm_stop", "summarization")
|
|
106
|
-
# @param agent [Symbol, String, nil] Agent name if applicable
|
|
107
|
-
# @param metadata [Hash] Additional context data
|
|
108
|
-
# @return [void]
|
|
109
|
-
def emit_error(error, source:, context:, agent: nil, **metadata)
|
|
110
|
-
emit(
|
|
111
|
-
type: "internal_error",
|
|
112
|
-
source: source,
|
|
113
|
-
context: context,
|
|
114
|
-
agent: agent,
|
|
115
|
-
error_class: error.class.name,
|
|
116
|
-
error_message: error.message,
|
|
117
|
-
backtrace: error.backtrace&.first(5),
|
|
118
|
-
**metadata,
|
|
119
|
-
)
|
|
120
|
-
rescue StandardError
|
|
121
|
-
# Absolute fallback - if emit_error itself fails, don't break execution
|
|
122
|
-
# This should never happen, but we must be defensive
|
|
123
|
-
nil
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
# Parser for agent markdown files with YAML frontmatter
|
|
5
|
-
#
|
|
6
|
-
# Supports two formats:
|
|
7
|
-
# 1. SwarmSDK format - YAML frontmatter with array-based tools
|
|
8
|
-
# 2. Claude Code format - Detected and converted via ClaudeCodeAgentAdapter
|
|
9
|
-
#
|
|
10
|
-
# Format detection is automatic based on frontmatter structure.
|
|
11
|
-
class MarkdownParser
|
|
12
|
-
FRONTMATTER_PATTERN = /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
|
|
13
|
-
|
|
14
|
-
class << self
|
|
15
|
-
# Parse markdown content into an Agent::Definition
|
|
16
|
-
#
|
|
17
|
-
# Automatically detects format (SwarmSDK or Claude Code) and routes
|
|
18
|
-
# to appropriate parser.
|
|
19
|
-
#
|
|
20
|
-
# @param content [String] Markdown content with YAML frontmatter
|
|
21
|
-
# @param agent_name [Symbol, String, nil] Name of the agent
|
|
22
|
-
# @return [Agent::Definition] Parsed agent definition
|
|
23
|
-
# @raise [ConfigurationError] if format is invalid
|
|
24
|
-
def parse(content, agent_name = nil)
|
|
25
|
-
# Detect Claude Code format and route to adapter
|
|
26
|
-
if ClaudeCodeAgentAdapter.claude_code_format?(content)
|
|
27
|
-
config = ClaudeCodeAgentAdapter.parse(content, agent_name)
|
|
28
|
-
# For Claude Code format, agent_name parameter is required since
|
|
29
|
-
# the 'name' field in frontmatter is Claude Code specific and not used
|
|
30
|
-
unless agent_name
|
|
31
|
-
raise ConfigurationError, "Agent name must be provided when parsing Claude Code format"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
Agent::Definition.new(agent_name.to_sym, config)
|
|
35
|
-
else
|
|
36
|
-
# Use standard SwarmSDK format parsing
|
|
37
|
-
new(content, agent_name).parse
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def initialize(content, agent_name = nil)
|
|
43
|
-
@content = content
|
|
44
|
-
@agent_name = agent_name
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def parse
|
|
48
|
-
if @content =~ FRONTMATTER_PATTERN
|
|
49
|
-
frontmatter_yaml = Regexp.last_match(1)
|
|
50
|
-
prompt_content = Regexp.last_match(2).strip
|
|
51
|
-
|
|
52
|
-
frontmatter = YAML.safe_load(frontmatter_yaml, permitted_classes: [Symbol], aliases: true)
|
|
53
|
-
|
|
54
|
-
unless frontmatter.is_a?(Hash)
|
|
55
|
-
raise ConfigurationError, "Invalid frontmatter format in agent definition"
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Symbolize keys for AgentDefinition
|
|
59
|
-
config = Utils.symbolize_keys(frontmatter).merge(system_prompt: prompt_content)
|
|
60
|
-
|
|
61
|
-
name = @agent_name || frontmatter["name"]
|
|
62
|
-
unless name
|
|
63
|
-
raise ConfigurationError, "Agent definition must include 'name' in frontmatter or be specified externally"
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Convert name to symbol
|
|
67
|
-
name = name.to_sym
|
|
68
|
-
|
|
69
|
-
Agent::Definition.new(name, config)
|
|
70
|
-
else
|
|
71
|
-
raise ConfigurationError, "Invalid Markdown agent definition format. Expected YAML frontmatter followed by prompt content."
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"sonnet": "claude-sonnet-4-5-20250929",
|
|
3
|
-
"opus": "claude-opus-4-1-20250805",
|
|
4
|
-
"haiku": "claude-haiku-4-5-20251001",
|
|
5
|
-
"claude-sonnet-4-5": "claude-sonnet-4-5-20250929",
|
|
6
|
-
"claude-opus-4-1": "claude-opus-4-1-20250805",
|
|
7
|
-
"claude-haiku-4-5": "claude-haiku-4-5-20251001"
|
|
8
|
-
}
|