swarm_sdk 2.7.13 → 3.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +43 -22
- data/lib/swarm_sdk/ruby_llm_patches/init.rb +6 -0
- data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
- data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +3 -4
- 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 +84 -148
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
- data/lib/swarm_sdk/agent/builder.rb +0 -680
- data/lib/swarm_sdk/agent/chat.rb +0 -1432
- 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 -581
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
- 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 -553
- 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/config.rb +0 -367
- data/lib/swarm_sdk/configuration/parser.rb +0 -397
- data/lib/swarm_sdk/configuration/translator.rb +0 -283
- 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 -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 -212
- 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 -195
- data/lib/swarm_sdk/swarm/builder.rb +0 -256
- data/lib/swarm_sdk/swarm/executor.rb +0 -290
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -151
- data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -360
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -270
- 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 -843
- 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 -718
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
module V3
|
|
5
|
+
# Global configuration for V3 agents
|
|
6
|
+
#
|
|
7
|
+
# Provides sensible defaults that can be overridden per-agent
|
|
8
|
+
# via AgentDefinition attributes. Also forwards provider API keys
|
|
9
|
+
# to RubyLLM when {#apply_provider_config!} is called.
|
|
10
|
+
#
|
|
11
|
+
# @example Configure defaults
|
|
12
|
+
# SwarmSDK::V3.configure do |config|
|
|
13
|
+
# config.default_model = "claude-sonnet-4"
|
|
14
|
+
# config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example Reset to defaults
|
|
18
|
+
# SwarmSDK::V3.reset_configuration!
|
|
19
|
+
class Configuration
|
|
20
|
+
# --- LLM defaults ---
|
|
21
|
+
|
|
22
|
+
# @return [String] Default LLM model for agents without explicit model
|
|
23
|
+
attr_accessor :default_model
|
|
24
|
+
|
|
25
|
+
# --- Provider API keys and endpoints ---
|
|
26
|
+
|
|
27
|
+
# @return [String, nil] OpenAI API key
|
|
28
|
+
attr_accessor :openai_api_key
|
|
29
|
+
|
|
30
|
+
# @return [String, nil] OpenAI custom API base URL
|
|
31
|
+
attr_accessor :openai_api_base
|
|
32
|
+
|
|
33
|
+
# @return [Boolean] Use "system" role instead of "developer" for OpenAI.
|
|
34
|
+
# OpenAI's API uses "developer" by default, but some OpenAI-compatible
|
|
35
|
+
# servers still require the traditional "system" role.
|
|
36
|
+
attr_accessor :openai_use_system_role
|
|
37
|
+
|
|
38
|
+
# @return [String, nil] Anthropic API key
|
|
39
|
+
attr_accessor :anthropic_api_key
|
|
40
|
+
|
|
41
|
+
# @return [String, nil] Anthropic custom API base URL
|
|
42
|
+
attr_accessor :anthropic_api_base
|
|
43
|
+
|
|
44
|
+
# @return [String, nil] Google Gemini API key
|
|
45
|
+
attr_accessor :gemini_api_key
|
|
46
|
+
|
|
47
|
+
# @return [String, nil] Gemini custom API base URL
|
|
48
|
+
attr_accessor :gemini_api_base
|
|
49
|
+
|
|
50
|
+
# @return [String, nil] AWS Bedrock access key
|
|
51
|
+
attr_accessor :bedrock_api_key
|
|
52
|
+
|
|
53
|
+
# @return [String, nil] AWS Bedrock secret key
|
|
54
|
+
attr_accessor :bedrock_secret_key
|
|
55
|
+
|
|
56
|
+
# @return [String, nil] AWS Bedrock region
|
|
57
|
+
attr_accessor :bedrock_region
|
|
58
|
+
|
|
59
|
+
# @return [String, nil] AWS Bedrock session token
|
|
60
|
+
attr_accessor :bedrock_session_token
|
|
61
|
+
|
|
62
|
+
# --- Background LLM configuration ---
|
|
63
|
+
|
|
64
|
+
# @return [String, nil] Model for background tasks (compression, summarization)
|
|
65
|
+
attr_accessor :background_model
|
|
66
|
+
|
|
67
|
+
# @return [String, nil] Provider for background model
|
|
68
|
+
attr_accessor :background_provider
|
|
69
|
+
|
|
70
|
+
# @return [String, nil] Custom API base URL for background model
|
|
71
|
+
attr_accessor :background_base_url
|
|
72
|
+
|
|
73
|
+
# @return [Hash] Custom HTTP headers for background model
|
|
74
|
+
attr_accessor :background_headers
|
|
75
|
+
|
|
76
|
+
# @return [Hash] Raw API body parameters for background model (temperature, etc.)
|
|
77
|
+
attr_accessor :background_parameters
|
|
78
|
+
|
|
79
|
+
# --- Subtask LLM configuration ---
|
|
80
|
+
|
|
81
|
+
# @return [String, nil] Model for subtask agents (nil = inherit from parent)
|
|
82
|
+
attr_accessor :subtask_model
|
|
83
|
+
|
|
84
|
+
# @return [String, nil] Provider for subtask model (nil = inherit from parent)
|
|
85
|
+
attr_accessor :subtask_provider
|
|
86
|
+
|
|
87
|
+
# @return [String, nil] Custom API base URL for subtask model
|
|
88
|
+
attr_accessor :subtask_base_url
|
|
89
|
+
|
|
90
|
+
# @return [Hash] Custom HTTP headers for subtask model
|
|
91
|
+
attr_accessor :subtask_headers
|
|
92
|
+
|
|
93
|
+
# @return [Hash] Raw API body parameters for subtask model (temperature, etc.)
|
|
94
|
+
attr_accessor :subtask_parameters
|
|
95
|
+
|
|
96
|
+
# --- Tool configuration ---
|
|
97
|
+
|
|
98
|
+
# @return [Array<Symbol>] Tools added to every agent (defaults to [])
|
|
99
|
+
attr_accessor :default_tools
|
|
100
|
+
|
|
101
|
+
# @return [Array<Symbol>, nil] Available tools (nil = all built-in tools)
|
|
102
|
+
attr_accessor :registered_tools
|
|
103
|
+
|
|
104
|
+
# @return [Integer, nil] Default max concurrent tool executions per agent.
|
|
105
|
+
# When set, agents execute multiple tool calls from a single LLM response
|
|
106
|
+
# concurrently using Async. nil disables concurrency (sequential execution).
|
|
107
|
+
attr_accessor :default_max_concurrent_tools
|
|
108
|
+
|
|
109
|
+
# --- Embedding configuration ---
|
|
110
|
+
|
|
111
|
+
# @return [String] Sentence-transformer model name for embeddings
|
|
112
|
+
attr_accessor :embedding_model
|
|
113
|
+
|
|
114
|
+
# @return [String, nil] Cache directory for downloaded embedding models
|
|
115
|
+
attr_accessor :embedding_cache_dir
|
|
116
|
+
|
|
117
|
+
# --- Memory defaults ---
|
|
118
|
+
|
|
119
|
+
# @return [Integer] Default number of recent turns to keep in short-term memory
|
|
120
|
+
attr_accessor :default_stm_turns
|
|
121
|
+
|
|
122
|
+
# @return [Integer] Default number of memory cards to retrieve per turn
|
|
123
|
+
attr_accessor :default_retrieval_top_k
|
|
124
|
+
|
|
125
|
+
# @return [Float] Default semantic weight for hybrid search (0.0-1.0)
|
|
126
|
+
attr_accessor :default_semantic_weight
|
|
127
|
+
|
|
128
|
+
# @return [Float] Default keyword weight for hybrid search (0.0-1.0)
|
|
129
|
+
attr_accessor :default_keyword_weight
|
|
130
|
+
|
|
131
|
+
# --- Exposure score weights ---
|
|
132
|
+
|
|
133
|
+
# @return [Float] Weight for frequency component in exposure score (α)
|
|
134
|
+
attr_accessor :exposure_frequency_weight
|
|
135
|
+
|
|
136
|
+
# @return [Float] Weight for recency component in exposure score (β)
|
|
137
|
+
attr_accessor :exposure_recency_weight
|
|
138
|
+
|
|
139
|
+
# @return [Float] Weight for dwell component in exposure score (γ)
|
|
140
|
+
attr_accessor :exposure_dwell_weight
|
|
141
|
+
|
|
142
|
+
# @return [Integer] Half-life for recency decay in seconds (default: 7 days)
|
|
143
|
+
attr_accessor :exposure_recency_half_life
|
|
144
|
+
|
|
145
|
+
# --- Memory lifecycle tuning ---
|
|
146
|
+
#
|
|
147
|
+
# These parameters control how often the agent runs background memory
|
|
148
|
+
# maintenance tasks (compression, consolidation, promotion) during
|
|
149
|
+
# conversation. Lower intervals mean more frequent maintenance.
|
|
150
|
+
|
|
151
|
+
# How often the agent compresses low-exposure memory cards.
|
|
152
|
+
# Compression advances cards through the L0→L4 ladder, reducing
|
|
153
|
+
# storage and context window usage. Lower values compress more
|
|
154
|
+
# aggressively (every 3 turns vs every 10).
|
|
155
|
+
#
|
|
156
|
+
# @return [Integer] Run compression every N turns (default: 5)
|
|
157
|
+
attr_accessor :compression_interval
|
|
158
|
+
|
|
159
|
+
# How often the agent runs consolidation (dedup, cluster updates,
|
|
160
|
+
# conflict detection). Consolidation merges near-duplicate cards,
|
|
161
|
+
# updates cluster rolling summaries, and creates `contradicts`
|
|
162
|
+
# edges between conflicting cards.
|
|
163
|
+
#
|
|
164
|
+
# @return [Integer] Run consolidation every N turns (default: 10)
|
|
165
|
+
attr_accessor :consolidation_interval
|
|
166
|
+
|
|
167
|
+
# Minimum access count before a compressed card (L1-L4) is promoted
|
|
168
|
+
# back toward L0. Promotion rebuilds richer text from graph neighbors
|
|
169
|
+
# and LLM, counterbalancing compression for frequently-accessed cards.
|
|
170
|
+
#
|
|
171
|
+
# @return [Integer] Min access count for promotion (default: 5)
|
|
172
|
+
attr_accessor :promotion_access_threshold
|
|
173
|
+
|
|
174
|
+
# --- Retrieval tuning ---
|
|
175
|
+
#
|
|
176
|
+
# These parameters control hybrid search: how semantic embeddings and
|
|
177
|
+
# BM25 keyword scores are combined, and how graph expansion works.
|
|
178
|
+
|
|
179
|
+
# Reciprocal Rank Fusion (RRF) constant. Controls how semantic and
|
|
180
|
+
# keyword search ranks are combined. Higher values flatten rank
|
|
181
|
+
# differences, giving later results more weight relative to top hits.
|
|
182
|
+
# Standard value from the RRF paper is 60.
|
|
183
|
+
#
|
|
184
|
+
# @return [Integer] RRF constant k (default: 60)
|
|
185
|
+
attr_accessor :rrf_k
|
|
186
|
+
|
|
187
|
+
# BM25 term frequency saturation parameter (k1). Controls how quickly
|
|
188
|
+
# repeated terms in a document contribute diminishing returns.
|
|
189
|
+
# Higher values give more weight to repeated terms. Standard
|
|
190
|
+
# Okapi BM25 default is 1.2.
|
|
191
|
+
#
|
|
192
|
+
# @return [Float] BM25 k1 parameter (default: 1.2)
|
|
193
|
+
attr_accessor :bm25_k1
|
|
194
|
+
|
|
195
|
+
# BM25 document length normalization parameter (b). Controls how
|
|
196
|
+
# much longer documents are penalized relative to average length.
|
|
197
|
+
# 0.0 = no penalty (all docs treated equally), 1.0 = full penalty.
|
|
198
|
+
# Standard Okapi BM25 default is 0.75.
|
|
199
|
+
#
|
|
200
|
+
# @return [Float] BM25 b parameter (default: 0.75)
|
|
201
|
+
attr_accessor :bm25_b
|
|
202
|
+
|
|
203
|
+
# Extra score boost per entity match in keyword search. When a
|
|
204
|
+
# query term appears in a card's entity list (not just its text),
|
|
205
|
+
# this boost is added to the BM25 score. Helps surface cards about
|
|
206
|
+
# specific named entities like "JWT", "PostgreSQL", etc.
|
|
207
|
+
#
|
|
208
|
+
# @return [Float] Entity match boost per match (default: 0.5)
|
|
209
|
+
attr_accessor :bm25_entity_boost
|
|
210
|
+
|
|
211
|
+
# Maximum 1-hop graph neighbors to expand per seed card during
|
|
212
|
+
# retrieval. After finding the top-k cards, the retriever traverses
|
|
213
|
+
# graph edges to pull in related cards. This caps how many neighbors
|
|
214
|
+
# any single card can contribute, preventing highly-connected cards
|
|
215
|
+
# from dominating results.
|
|
216
|
+
#
|
|
217
|
+
# @return [Integer] Max neighbors per seed (default: 4)
|
|
218
|
+
attr_accessor :max_neighbors_per_seed
|
|
219
|
+
|
|
220
|
+
# --- Context builder tuning ---
|
|
221
|
+
#
|
|
222
|
+
# These parameters control how the working context is assembled from
|
|
223
|
+
# memory tiers: retrieved cards, exploration sprinkle, and dedup.
|
|
224
|
+
|
|
225
|
+
# Number of low-exposure "anti-forgetting" cards sprinkled into each
|
|
226
|
+
# context build. These are rarely-accessed cards with some relevance
|
|
227
|
+
# to the query, included to prevent permanent forgetting. Set to 0
|
|
228
|
+
# to disable exploration entirely.
|
|
229
|
+
#
|
|
230
|
+
# @return [Integer] Exploration sprinkle size (default: 2)
|
|
231
|
+
attr_accessor :exploration_sample_size
|
|
232
|
+
|
|
233
|
+
# Minimum embedding similarity between the query and an exploration
|
|
234
|
+
# candidate card. Lower values include more diverse (less relevant)
|
|
235
|
+
# cards in the sprinkle. Higher values restrict to cards that are
|
|
236
|
+
# at least loosely relevant.
|
|
237
|
+
#
|
|
238
|
+
# @return [Float] Min similarity for exploration (default: 0.15)
|
|
239
|
+
attr_accessor :exploration_min_similarity
|
|
240
|
+
|
|
241
|
+
# Similarity threshold above which cards are considered near-duplicates
|
|
242
|
+
# during context assembly. When two cards in the working context have
|
|
243
|
+
# similarity above this threshold, the duplicate is removed. Uses the
|
|
244
|
+
# adapter's {Adapters::Base#similarity} method.
|
|
245
|
+
#
|
|
246
|
+
# @return [Float] Dedup threshold (default: 0.92)
|
|
247
|
+
attr_accessor :dedup_similarity_threshold
|
|
248
|
+
|
|
249
|
+
# @return [Boolean] Default associative memory setting for agents.
|
|
250
|
+
# When enabled, exploration cards are labeled distinctly and guidance
|
|
251
|
+
# is injected encouraging the LLM to naturally surface tangential memories.
|
|
252
|
+
attr_accessor :default_associative_memory
|
|
253
|
+
|
|
254
|
+
# --- Ingestion tuning ---
|
|
255
|
+
#
|
|
256
|
+
# These parameters control how new conversation turns are processed
|
|
257
|
+
# into memory cards, entities, and graph edges.
|
|
258
|
+
|
|
259
|
+
# Minimum embedding similarity for creating `same_entity` edges
|
|
260
|
+
# between new cards and existing cards during cross-turn entity
|
|
261
|
+
# linking. Lower values create more connections but may produce
|
|
262
|
+
# false positives.
|
|
263
|
+
#
|
|
264
|
+
# @return [Float] Min similarity for entity edges (default: 0.3)
|
|
265
|
+
attr_accessor :entity_edge_min_similarity
|
|
266
|
+
|
|
267
|
+
# Maximum number of existing cards to scan when creating cross-turn
|
|
268
|
+
# entity edges. Limits the O(n*m) scan to prevent slowdowns as the
|
|
269
|
+
# memory store grows. Scans the most recent cards first.
|
|
270
|
+
#
|
|
271
|
+
# @return [Integer] Cross-turn edge scan limit (default: 50)
|
|
272
|
+
attr_accessor :cross_turn_edge_scan_limit
|
|
273
|
+
|
|
274
|
+
# --- Consolidation tuning ---
|
|
275
|
+
#
|
|
276
|
+
# These parameters control periodic maintenance: dedup, merging,
|
|
277
|
+
# cluster updates, and conflict detection.
|
|
278
|
+
|
|
279
|
+
# Similarity threshold for detecting duplicate cards during
|
|
280
|
+
# consolidation. Cards with vector similarity above this threshold
|
|
281
|
+
# are merged into a canonical card. Lower values merge more
|
|
282
|
+
# aggressively but risk merging distinct-but-similar concepts.
|
|
283
|
+
#
|
|
284
|
+
# @return [Float] Consolidator dedup threshold (default: 0.92)
|
|
285
|
+
attr_accessor :consolidator_dedup_threshold
|
|
286
|
+
|
|
287
|
+
# Minimum similarity for considering two cards as potential conflicts.
|
|
288
|
+
# The conflict band is: conflict_threshold <= similarity < dedup_threshold.
|
|
289
|
+
# Raise to reduce false positives; lower to catch subtler contradictions.
|
|
290
|
+
#
|
|
291
|
+
# @return [Float] Min similarity for conflict detection (default: 0.75)
|
|
292
|
+
attr_accessor :consolidator_conflict_threshold
|
|
293
|
+
|
|
294
|
+
# --- MCP settings ---
|
|
295
|
+
|
|
296
|
+
# @return [Boolean] Global SSL verification for MCP HTTP connections (default: true).
|
|
297
|
+
# OpenSSL 3.6 enforces CRL checking by default, which breaks most HTTPS MCP
|
|
298
|
+
# endpoints. Set to false to disable verification for local development.
|
|
299
|
+
# Can be overridden per-server via ServerDefinition's ssl_verify option.
|
|
300
|
+
attr_accessor :mcp_ssl_verify
|
|
301
|
+
|
|
302
|
+
# --- Subtask limits ---
|
|
303
|
+
|
|
304
|
+
# @return [Integer] Maximum nesting depth for SubTask spawning (default: 1)
|
|
305
|
+
attr_accessor :max_subtask_depth
|
|
306
|
+
|
|
307
|
+
# --- Tool limits ---
|
|
308
|
+
|
|
309
|
+
# @return [Integer] Maximum output character limit for tool results
|
|
310
|
+
attr_accessor :output_character_limit
|
|
311
|
+
|
|
312
|
+
# @return [Integer] Maximum line character limit for Read tool
|
|
313
|
+
attr_accessor :line_character_limit
|
|
314
|
+
|
|
315
|
+
# @return [Integer] Default line limit for Read tool
|
|
316
|
+
attr_accessor :read_line_limit
|
|
317
|
+
|
|
318
|
+
# @return [Integer] Maximum results from Glob tool
|
|
319
|
+
attr_accessor :glob_result_limit
|
|
320
|
+
|
|
321
|
+
# @return [Integer] Default Bash command timeout in milliseconds
|
|
322
|
+
attr_accessor :bash_command_timeout
|
|
323
|
+
|
|
324
|
+
# @return [Integer] Maximum Bash command timeout in milliseconds
|
|
325
|
+
attr_accessor :bash_command_max_timeout
|
|
326
|
+
|
|
327
|
+
def initialize
|
|
328
|
+
reset!
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Reset all configuration to defaults
|
|
332
|
+
#
|
|
333
|
+
# @return [void]
|
|
334
|
+
def reset!
|
|
335
|
+
# LLM defaults
|
|
336
|
+
@default_model = "claude-sonnet-4"
|
|
337
|
+
|
|
338
|
+
# Provider keys
|
|
339
|
+
@openai_api_key = nil
|
|
340
|
+
@openai_api_base = nil
|
|
341
|
+
@openai_use_system_role = false
|
|
342
|
+
@anthropic_api_key = nil
|
|
343
|
+
@anthropic_api_base = nil
|
|
344
|
+
@gemini_api_key = nil
|
|
345
|
+
@gemini_api_base = nil
|
|
346
|
+
@bedrock_api_key = nil
|
|
347
|
+
@bedrock_secret_key = nil
|
|
348
|
+
@bedrock_region = nil
|
|
349
|
+
@bedrock_session_token = nil
|
|
350
|
+
|
|
351
|
+
# Background LLM
|
|
352
|
+
@background_model = nil
|
|
353
|
+
@background_provider = nil
|
|
354
|
+
@background_base_url = nil
|
|
355
|
+
@background_headers = {}
|
|
356
|
+
@background_parameters = {}
|
|
357
|
+
|
|
358
|
+
# Subtask LLM
|
|
359
|
+
@subtask_model = nil
|
|
360
|
+
@subtask_provider = nil
|
|
361
|
+
@subtask_base_url = nil
|
|
362
|
+
@subtask_headers = {}
|
|
363
|
+
@subtask_parameters = {}
|
|
364
|
+
|
|
365
|
+
# Tools
|
|
366
|
+
@default_tools = []
|
|
367
|
+
@registered_tools = nil
|
|
368
|
+
@default_max_concurrent_tools = nil
|
|
369
|
+
|
|
370
|
+
# Embedding
|
|
371
|
+
@embedding_model = "sentence-transformers/multi-qa-MiniLM-L6-cos-v1"
|
|
372
|
+
@embedding_cache_dir = nil
|
|
373
|
+
|
|
374
|
+
# Memory retrieval (optimized via manual-memory-evals.md Phases 1-2)
|
|
375
|
+
@default_stm_turns = 8
|
|
376
|
+
@default_retrieval_top_k = 10 # was 15 — less noise, better precision
|
|
377
|
+
@default_semantic_weight = 0.4 # was 0.5 — slightly keyword-leaning for entity recall
|
|
378
|
+
@default_keyword_weight = 0.6 # was 0.5 — better for named entities and numbers
|
|
379
|
+
|
|
380
|
+
# Exposure score weights (additive: α·frequency + β·recency + γ·dwell)
|
|
381
|
+
@exposure_frequency_weight = 0.4
|
|
382
|
+
@exposure_recency_weight = 0.4
|
|
383
|
+
@exposure_dwell_weight = 0.2
|
|
384
|
+
@exposure_recency_half_life = 7 * 24 * 3600 # 7 days in seconds
|
|
385
|
+
|
|
386
|
+
# Memory lifecycle
|
|
387
|
+
@compression_interval = 5
|
|
388
|
+
@consolidation_interval = 10
|
|
389
|
+
@promotion_access_threshold = 5
|
|
390
|
+
|
|
391
|
+
# Retrieval (optimized via manual-memory-evals.md Phases 3-4)
|
|
392
|
+
@rrf_k = 120 # was 60 — flatter ranking gives more diverse results
|
|
393
|
+
@bm25_k1 = 1.2
|
|
394
|
+
@bm25_b = 1.0 # was 0.75 — full length normalization for short memory cards
|
|
395
|
+
@bm25_entity_boost = 0.5
|
|
396
|
+
@max_neighbors_per_seed = 4
|
|
397
|
+
|
|
398
|
+
# Context builder
|
|
399
|
+
@exploration_sample_size = 2
|
|
400
|
+
@exploration_min_similarity = 0.15
|
|
401
|
+
@dedup_similarity_threshold = 0.92
|
|
402
|
+
@default_associative_memory = false
|
|
403
|
+
|
|
404
|
+
# Ingestion
|
|
405
|
+
@entity_edge_min_similarity = 0.3
|
|
406
|
+
@cross_turn_edge_scan_limit = 50
|
|
407
|
+
|
|
408
|
+
# Consolidation
|
|
409
|
+
@consolidator_dedup_threshold = 0.92
|
|
410
|
+
@consolidator_conflict_threshold = 0.75
|
|
411
|
+
|
|
412
|
+
# MCP
|
|
413
|
+
@mcp_ssl_verify = true
|
|
414
|
+
|
|
415
|
+
# Subtask limits
|
|
416
|
+
@max_subtask_depth = 1
|
|
417
|
+
|
|
418
|
+
# Tool limits
|
|
419
|
+
@output_character_limit = 30_000
|
|
420
|
+
@line_character_limit = 2_000
|
|
421
|
+
@read_line_limit = 2_000
|
|
422
|
+
@glob_result_limit = 500
|
|
423
|
+
@bash_command_timeout = 120_000
|
|
424
|
+
@bash_command_max_timeout = 600_000
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# Forward provider API keys and settings to RubyLLM
|
|
428
|
+
#
|
|
429
|
+
# Call this after setting provider keys to configure RubyLLM.
|
|
430
|
+
# Only sets values that are non-nil.
|
|
431
|
+
#
|
|
432
|
+
# @return [void]
|
|
433
|
+
#
|
|
434
|
+
# @example
|
|
435
|
+
# config = SwarmSDK::V3::Configuration.instance
|
|
436
|
+
# config.anthropic_api_key = "sk-ant-..."
|
|
437
|
+
# config.apply_provider_config!
|
|
438
|
+
def apply_provider_config!
|
|
439
|
+
RubyLLM.configure do |c|
|
|
440
|
+
c.openai_api_key = @openai_api_key if @openai_api_key
|
|
441
|
+
c.openai_api_base = @openai_api_base if @openai_api_base
|
|
442
|
+
c.openai_use_system_role = @openai_use_system_role
|
|
443
|
+
c.anthropic_api_key = @anthropic_api_key if @anthropic_api_key
|
|
444
|
+
c.anthropic_api_base = @anthropic_api_base if @anthropic_api_base
|
|
445
|
+
c.gemini_api_key = @gemini_api_key if @gemini_api_key
|
|
446
|
+
c.gemini_api_base = @gemini_api_base if @gemini_api_base
|
|
447
|
+
c.bedrock_api_key = @bedrock_api_key if @bedrock_api_key
|
|
448
|
+
c.bedrock_secret_key = @bedrock_secret_key if @bedrock_secret_key
|
|
449
|
+
c.bedrock_region = @bedrock_region if @bedrock_region
|
|
450
|
+
c.bedrock_session_token = @bedrock_session_token if @bedrock_session_token
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
class << self
|
|
455
|
+
# Get the global configuration instance
|
|
456
|
+
#
|
|
457
|
+
# @return [Configuration]
|
|
458
|
+
def instance
|
|
459
|
+
@instance ||= new
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Configure global settings
|
|
463
|
+
#
|
|
464
|
+
# Yields the configuration instance, then applies provider settings
|
|
465
|
+
# to RubyLLM automatically.
|
|
466
|
+
#
|
|
467
|
+
# @yield [Configuration] The configuration instance
|
|
468
|
+
# @return [Configuration]
|
|
469
|
+
#
|
|
470
|
+
# @example
|
|
471
|
+
# SwarmSDK::V3::Configuration.configure do |config|
|
|
472
|
+
# config.default_model = "claude-sonnet-4"
|
|
473
|
+
# config.anthropic_api_key = "sk-ant-..."
|
|
474
|
+
# end
|
|
475
|
+
def configure
|
|
476
|
+
yield(instance) if block_given?
|
|
477
|
+
instance.apply_provider_config!
|
|
478
|
+
instance
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Reset configuration to defaults
|
|
482
|
+
#
|
|
483
|
+
# @return [void]
|
|
484
|
+
def reset!
|
|
485
|
+
instance.reset!
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
module V3
|
|
5
|
+
# Lightweight debug logging for V3 agents
|
|
6
|
+
#
|
|
7
|
+
# Activated by setting the `DEBUG` or `SWARM_DEBUG` environment variable.
|
|
8
|
+
# Outputs timestamped debug messages to stderr with timing information
|
|
9
|
+
# for performance profiling.
|
|
10
|
+
#
|
|
11
|
+
# @example Enable debug logging
|
|
12
|
+
# DEBUG=1 bundle exec ruby my_script.rb
|
|
13
|
+
#
|
|
14
|
+
# @example Use in code
|
|
15
|
+
# DebugLog.log("retriever", "semantic search found 15 cards")
|
|
16
|
+
# DebugLog.time("retriever", "keyword_search") { keyword_search(query) }
|
|
17
|
+
module DebugLog
|
|
18
|
+
class << self
|
|
19
|
+
# Whether debug logging is enabled
|
|
20
|
+
#
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
def enabled?
|
|
23
|
+
return @enabled if defined?(@enabled)
|
|
24
|
+
|
|
25
|
+
@enabled = ENV["DEBUG"] == "1" || ENV["SWARM_DEBUG"] == "1"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Log a debug message to stderr
|
|
29
|
+
#
|
|
30
|
+
# @param component [String] Component name (e.g., "retriever", "ingestion")
|
|
31
|
+
# @param message [String] Debug message
|
|
32
|
+
# @return [void]
|
|
33
|
+
#
|
|
34
|
+
# @example
|
|
35
|
+
# DebugLog.log("agent", "initializing memory store")
|
|
36
|
+
def log(component, message)
|
|
37
|
+
return unless enabled?
|
|
38
|
+
|
|
39
|
+
timestamp = Time.now.strftime("%H:%M:%S.%L")
|
|
40
|
+
warn("[DEBUG #{timestamp}] [#{component}] #{message}")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Execute a block and log its execution time
|
|
44
|
+
#
|
|
45
|
+
# @param component [String] Component name
|
|
46
|
+
# @param label [String] Operation label
|
|
47
|
+
# @yield Block to time
|
|
48
|
+
# @return [Object] Block return value
|
|
49
|
+
#
|
|
50
|
+
# @example
|
|
51
|
+
# result = DebugLog.time("retriever", "semantic_search") do
|
|
52
|
+
# adapter.vector_search(embedding, top_k: 30)
|
|
53
|
+
# end
|
|
54
|
+
def time(component, label)
|
|
55
|
+
unless enabled?
|
|
56
|
+
return yield
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
log(component, "#{label} START")
|
|
60
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
61
|
+
result = yield
|
|
62
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
63
|
+
log(component, "#{label} DONE (#{format_duration(elapsed)})")
|
|
64
|
+
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Format duration in human-readable form
|
|
71
|
+
#
|
|
72
|
+
# @param seconds [Float] Duration in seconds
|
|
73
|
+
# @return [String] Formatted duration
|
|
74
|
+
def format_duration(seconds)
|
|
75
|
+
if seconds < 0.001
|
|
76
|
+
"#{(seconds * 1_000_000).round}us"
|
|
77
|
+
elsif seconds < 1.0
|
|
78
|
+
"#{(seconds * 1_000).round(1)}ms"
|
|
79
|
+
else
|
|
80
|
+
"#{seconds.round(2)}s"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
module V3
|
|
5
|
+
# EventStream provides fiber-local event emission for V3 agents
|
|
6
|
+
#
|
|
7
|
+
# Thread-safe and fiber-safe event system using Fiber storage.
|
|
8
|
+
# Each fiber gets its own emitter instance, preventing cross-fiber contamination.
|
|
9
|
+
#
|
|
10
|
+
# Two emitter slots are available:
|
|
11
|
+
# - **Global emitter** (via `emitter=`): For logging/monitoring across all operations
|
|
12
|
+
# - **Block emitter** (via `block_emitter=`): For per-call event handling in `ask()` blocks
|
|
13
|
+
#
|
|
14
|
+
# @example Emit an event
|
|
15
|
+
# EventStream.emit(type: "agent_start", agent: :backend, model: "claude-sonnet-4")
|
|
16
|
+
#
|
|
17
|
+
# @example Subscribe to events globally
|
|
18
|
+
# EventStream.emitter = ->(event) { puts event.inspect }
|
|
19
|
+
# agent.ask("Hello")
|
|
20
|
+
# EventStream.reset!
|
|
21
|
+
#
|
|
22
|
+
# @example Receive events via block (set internally by Agent#ask)
|
|
23
|
+
# agent.ask("Hello") do |event|
|
|
24
|
+
# case event[:type]
|
|
25
|
+
# when "content_chunk"
|
|
26
|
+
# print event[:content]
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
module EventStream
|
|
30
|
+
FIBER_KEY = :v3_event_stream_emitter
|
|
31
|
+
BLOCK_KEY = :v3_event_stream_block_emitter
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# Emit a structured event
|
|
35
|
+
#
|
|
36
|
+
# Adds timestamp and forwards to both the global emitter and block emitter.
|
|
37
|
+
# No-op if no emitters are configured.
|
|
38
|
+
#
|
|
39
|
+
# @param data [Hash] Event data (type, agent, and event-specific fields)
|
|
40
|
+
# @return [void]
|
|
41
|
+
#
|
|
42
|
+
# @example
|
|
43
|
+
# EventStream.emit(
|
|
44
|
+
# type: "tool_call",
|
|
45
|
+
# agent: :backend,
|
|
46
|
+
# tool: "Read",
|
|
47
|
+
# arguments: { file_path: "src/main.rb" },
|
|
48
|
+
# )
|
|
49
|
+
def emit(**data)
|
|
50
|
+
global = Fiber[FIBER_KEY]
|
|
51
|
+
block = Fiber[BLOCK_KEY]
|
|
52
|
+
return if global.nil? && block.nil?
|
|
53
|
+
|
|
54
|
+
entry = data.merge(timestamp: Time.now.utc.iso8601(6)).compact
|
|
55
|
+
|
|
56
|
+
call_emitter(global, entry)
|
|
57
|
+
call_emitter(block, entry)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Set the global emitter for the current fiber
|
|
61
|
+
#
|
|
62
|
+
# Use this for logging, monitoring, and analytics that should
|
|
63
|
+
# receive all events regardless of where they originate.
|
|
64
|
+
#
|
|
65
|
+
# @param callable [#call] Object responding to call(Hash)
|
|
66
|
+
# @return [void]
|
|
67
|
+
def emitter=(callable)
|
|
68
|
+
Fiber[FIBER_KEY] = callable
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Get the current global emitter
|
|
72
|
+
#
|
|
73
|
+
# @return [#call, nil] Current emitter or nil
|
|
74
|
+
def emitter
|
|
75
|
+
Fiber[FIBER_KEY]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Set the block emitter for the current fiber
|
|
79
|
+
#
|
|
80
|
+
# Used internally by Agent#ask to wire the user's block to receive events.
|
|
81
|
+
# Not typically called directly by SDK users.
|
|
82
|
+
#
|
|
83
|
+
# @param callable [#call, nil] Object responding to call(Hash), or nil to clear
|
|
84
|
+
# @return [void]
|
|
85
|
+
def block_emitter=(callable)
|
|
86
|
+
Fiber[BLOCK_KEY] = callable
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Get the current block emitter
|
|
90
|
+
#
|
|
91
|
+
# @return [#call, nil] Current block emitter or nil
|
|
92
|
+
def block_emitter
|
|
93
|
+
Fiber[BLOCK_KEY]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Reset both emitters for the current fiber
|
|
97
|
+
#
|
|
98
|
+
# @return [void]
|
|
99
|
+
def reset!
|
|
100
|
+
Fiber[FIBER_KEY] = nil
|
|
101
|
+
Fiber[BLOCK_KEY] = nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Check if any emitter is configured
|
|
105
|
+
#
|
|
106
|
+
# @return [Boolean]
|
|
107
|
+
def enabled?
|
|
108
|
+
!Fiber[FIBER_KEY].nil? || !Fiber[BLOCK_KEY].nil?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# Call an emitter with error handling
|
|
114
|
+
#
|
|
115
|
+
# @param emitter [#call, nil] Emitter to call
|
|
116
|
+
# @param entry [Hash] Event data to pass
|
|
117
|
+
# @return [void]
|
|
118
|
+
def call_emitter(emitter, entry)
|
|
119
|
+
return unless emitter
|
|
120
|
+
|
|
121
|
+
emitter.call(entry)
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
# Never let event emission break agent execution, but log to stderr
|
|
124
|
+
# so failures are visible during debugging rather than silently lost.
|
|
125
|
+
warn("[SwarmSDK::V3::EventStream] Emitter error: #{e.class}: #{e.message}")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|