swarm_sdk 2.7.14 → 3.0.0.alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -1,241 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Result
5
- attr_reader :content, :agent, :duration, :logs, :error, :metadata
6
-
7
- def initialize(content: nil, agent:, cost: nil, tokens: nil, duration: 0.0, logs: [], error: nil, metadata: {})
8
- @content = content
9
- @agent = agent
10
- @duration = duration
11
- @logs = logs
12
- @error = error
13
- @metadata = metadata
14
- # Legacy parameters kept for backward compatibility but not stored
15
- # Use total_cost and tokens methods instead which calculate from logs
16
- end
17
-
18
- def success?
19
- @error.nil?
20
- end
21
-
22
- def failure?
23
- !success?
24
- end
25
-
26
- # Check if execution was interrupted via swarm.stop
27
- #
28
- # @return [Boolean] true if execution was interrupted
29
- #
30
- # @example
31
- # result = swarm.execute("Build auth")
32
- # result.interrupted? # => false
33
- #
34
- # # After calling swarm.stop during execution:
35
- # result.interrupted? # => true
36
- def interrupted?
37
- @metadata[:interrupted] == true
38
- end
39
-
40
- # Get the reason execution finished
41
- #
42
- # Possible values:
43
- # - "finished" - Normal completion
44
- # - "interrupted" - Stopped via swarm.stop
45
- # - "error" - Execution failed with an error
46
- #
47
- # @return [String] The finish reason
48
- #
49
- # @example
50
- # result.finish_reason # => "finished"
51
- def finish_reason
52
- @metadata[:finish_reason] || (success? ? "finished" : "error")
53
- end
54
-
55
- # Calculate total cost from logs
56
- #
57
- # Delegates to total_cost for consistency. This attribute is calculated
58
- # dynamically rather than stored.
59
- #
60
- # @return [Float] Total cost in dollars
61
- def cost
62
- total_cost
63
- end
64
-
65
- # Get token breakdown from logs
66
- #
67
- # Returns input and output tokens from the last log entry with usage data.
68
- # This attribute is calculated dynamically rather than stored.
69
- #
70
- # @return [Hash] Token breakdown with :input and :output keys, or empty hash if no usage data
71
- def tokens
72
- last_entry = @logs.reverse.find { |entry| entry.dig(:usage, :cumulative_input_tokens) }
73
- return {} unless last_entry
74
-
75
- {
76
- input: last_entry.dig(:usage, :cumulative_input_tokens) || 0,
77
- output: last_entry.dig(:usage, :cumulative_output_tokens) || 0,
78
- }
79
- end
80
-
81
- def to_h
82
- {
83
- content: @content,
84
- agent: @agent,
85
- cost: cost,
86
- tokens: tokens,
87
- duration: @duration,
88
- success: success?,
89
- error: @error&.message,
90
- metadata: @metadata,
91
- }.compact
92
- end
93
-
94
- def to_json(*args)
95
- to_h.to_json(*args)
96
- end
97
-
98
- # Calculate total cost across all LLM responses
99
- #
100
- # Cost accumulation works as follows:
101
- # - Input cost: The LAST response's input_cost already includes the cost for the
102
- # full conversation history (all previous messages + current context)
103
- # - Output cost: Each response generates NEW tokens, so we SUM all output_costs
104
- # - Total = Last input_cost + Sum of all output_costs
105
- #
106
- # IMPORTANT: Do NOT sum total_cost across all entries - that would count
107
- # input costs multiple times since each call includes the full history!
108
- def total_cost
109
- entries_with_usage = @logs.select { |entry| entry.dig(:usage, :total_cost) }
110
- return 0.0 if entries_with_usage.empty?
111
-
112
- # Last entry's input cost (includes full conversation history)
113
- last_input_cost = entries_with_usage.last.dig(:usage, :input_cost) || 0.0
114
-
115
- # Sum all output costs (each response generates new tokens)
116
- total_output_cost = entries_with_usage.sum { |entry| entry.dig(:usage, :output_cost) || 0.0 }
117
-
118
- last_input_cost + total_output_cost
119
- end
120
-
121
- # Get total tokens from the last LLM response with cumulative tracking
122
- #
123
- # Token accumulation works as follows:
124
- # - Input tokens: Each API call sends the full conversation history, so the latest
125
- # response's cumulative_input_tokens already represents the full context
126
- # - Output tokens: Each response generates new tokens, cumulative_output_tokens sums them
127
- # - The cumulative_total_tokens in the last response already does this correctly
128
- #
129
- # IMPORTANT: Do NOT sum total_tokens across all log entries - that would count
130
- # input tokens multiple times since each call includes the full history!
131
- def total_tokens
132
- last_entry = @logs.reverse.find { |entry| entry.dig(:usage, :cumulative_total_tokens) }
133
- last_entry&.dig(:usage, :cumulative_total_tokens) || 0
134
- end
135
-
136
- # Get list of all agents involved in execution
137
- def agents_involved
138
- @logs.map { |entry| entry[:agent] }.compact.uniq.map(&:to_sym)
139
- end
140
-
141
- # Generate an LLM-readable transcript of the conversation
142
- #
143
- # Converts the execution logs into a human/LLM-readable format suitable for
144
- # reflection, analysis, memory creation, or passing to another agent.
145
- #
146
- # @param agents [Array<Symbol>] Optional list of agents to filter by.
147
- # If no agents specified, includes all agents.
148
- # If one or more agents specified, only includes events from those agents.
149
- # @param include_tool_results [Boolean] Include tool execution results (default: true)
150
- # @param include_thinking [Boolean] Include agent_step content/thinking (default: false)
151
- # @return [String] Formatted transcript ready for LLM consumption
152
- #
153
- # @example Get full transcript
154
- # result.transcript
155
- # # => "USER: Help me with CORS\n\nAGENT [assistant]: ..."
156
- #
157
- # @example Filter to specific agents
158
- # result.transcript(:backend, :database)
159
- # # => Only events from backend and database agents
160
- #
161
- # @example Single agent transcript
162
- # result.transcript(:backend)
163
- # # => Only events from backend agent
164
- #
165
- # @example Include thinking steps
166
- # result.transcript(include_thinking: true)
167
- # # => Includes agent_step intermediate reasoning
168
- def transcript(*agents, include_tool_results: true, include_thinking: false)
169
- agent_filter = agents.empty? ? nil : agents
170
- TranscriptBuilder.build(
171
- @logs,
172
- agents: agent_filter,
173
- include_tool_results: include_tool_results,
174
- include_thinking: include_thinking,
175
- )
176
- end
177
-
178
- # Get per-agent usage breakdown from logs
179
- #
180
- # Aggregates context usage, tokens, and cost for each agent from their
181
- # final agent_stop or agent_step events. Each agent's entry includes:
182
- # - input_tokens, output_tokens, total_tokens
183
- # - context_limit, usage_percentage, tokens_remaining
184
- # - input_cost, output_cost, total_cost
185
- #
186
- # @return [Hash{Symbol => Hash}] Per-agent usage breakdown
187
- #
188
- # @example
189
- # result.per_agent_usage[:backend]
190
- # # => {
191
- # # input_tokens: 15000,
192
- # # output_tokens: 5000,
193
- # # total_tokens: 20000,
194
- # # context_limit: 200000,
195
- # # usage_percentage: "10.0%",
196
- # # tokens_remaining: 180000,
197
- # # input_cost: 0.045,
198
- # # output_cost: 0.075,
199
- # # total_cost: 0.12
200
- # # }
201
- def per_agent_usage
202
- # Find the last usage entry for each agent
203
- agent_entries = {}
204
-
205
- @logs.each do |entry|
206
- next unless entry[:usage] && entry[:agent]
207
- next unless entry[:type] == "agent_step" || entry[:type] == "agent_stop"
208
-
209
- agent_name = entry[:agent].to_sym
210
- agent_entries[agent_name] = entry[:usage]
211
- end
212
-
213
- # Build breakdown from final usage entries
214
- agent_entries.transform_values do |usage|
215
- {
216
- input_tokens: usage[:cumulative_input_tokens] || 0,
217
- output_tokens: usage[:cumulative_output_tokens] || 0,
218
- total_tokens: usage[:cumulative_total_tokens] || 0,
219
- cached_tokens: usage[:cumulative_cached_tokens] || 0,
220
- context_limit: usage[:context_limit],
221
- usage_percentage: usage[:tokens_used_percentage],
222
- tokens_remaining: usage[:tokens_remaining],
223
- input_cost: usage[:input_cost] || 0.0,
224
- output_cost: usage[:output_cost] || 0.0,
225
- total_cost: usage[:total_cost] || 0.0,
226
- }
227
- end
228
- end
229
-
230
- # Count total LLM requests made
231
- # Each LLM API call produces either agent_step (tool calls) or agent_stop (final answer)
232
- def llm_requests
233
- @logs.count { |entry| entry[:type] == "agent_step" || entry[:type] == "agent_stop" }
234
- end
235
-
236
- # Count total tool calls made
237
- def tool_calls_count
238
- @logs.count { |entry| entry[:type] == "tool_call" }
239
- end
240
- end
241
- end
@@ -1,156 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Snapshot of swarm conversation state
5
- #
6
- # Encapsulates snapshot data with methods for serialization and deserialization.
7
- # Provides a clean API for saving/loading snapshots in various formats.
8
- #
9
- # @example Create and save snapshot
10
- # snapshot = swarm.snapshot
11
- # snapshot.write_to_file("session.json")
12
- #
13
- # @example Load and restore snapshot
14
- # snapshot = SwarmSDK::Snapshot.from_file("session.json")
15
- # result = swarm.restore(snapshot)
16
- #
17
- # @example Convert to/from hash
18
- # hash = snapshot.to_hash
19
- # snapshot = SwarmSDK::Snapshot.from_hash(hash)
20
- class Snapshot
21
- attr_reader :data
22
-
23
- # Initialize snapshot with data
24
- #
25
- # @param data [Hash] Snapshot data hash
26
- def initialize(data)
27
- @data = data
28
- end
29
-
30
- # Convert snapshot to hash
31
- #
32
- # @return [Hash] Snapshot data as hash
33
- def to_hash
34
- @data
35
- end
36
-
37
- # Convert snapshot to JSON string
38
- #
39
- # @param pretty [Boolean] Whether to pretty-print JSON (default: true)
40
- # @return [String] JSON string
41
- def to_json(pretty: true)
42
- if pretty
43
- JSON.pretty_generate(@data)
44
- else
45
- JSON.generate(@data)
46
- end
47
- end
48
-
49
- # Write snapshot to file as JSON
50
- #
51
- # Uses atomic write pattern (write to temp file, then rename) to prevent
52
- # corruption if process crashes during write.
53
- #
54
- # @param path [String] File path to write to
55
- # @param pretty [Boolean] Whether to pretty-print JSON (default: true)
56
- # @return [void]
57
- def write_to_file(path, pretty: true)
58
- # Atomic write: write to temp file, then rename
59
- # This ensures the snapshot file is never corrupted even if process crashes
60
- temp_path = "#{path}.tmp.#{Process.pid}.#{Time.now.to_i}.#{SecureRandom.hex(4)}"
61
-
62
- File.write(temp_path, to_json(pretty: pretty))
63
- File.rename(temp_path, path)
64
- rescue
65
- # Clean up temp file if it exists
66
- File.delete(temp_path) if File.exist?(temp_path)
67
- raise
68
- end
69
-
70
- class << self
71
- # Create snapshot from hash
72
- #
73
- # @param hash [Hash] Snapshot data hash
74
- # @return [Snapshot] New snapshot instance
75
- def from_hash(hash)
76
- new(hash)
77
- end
78
-
79
- # Create snapshot from JSON string
80
- #
81
- # @param json_string [String] JSON string
82
- # @return [Snapshot] New snapshot instance
83
- def from_json(json_string)
84
- hash = JSON.parse(json_string, symbolize_names: true)
85
- new(hash)
86
- end
87
-
88
- # Create snapshot from JSON file
89
- #
90
- # @param path [String] File path to read from
91
- # @return [Snapshot] New snapshot instance
92
- def from_file(path)
93
- json_string = File.read(path)
94
- from_json(json_string)
95
- end
96
- end
97
-
98
- # Get snapshot version
99
- #
100
- # @return [String] Snapshot version
101
- def version
102
- @data[:version] || @data["version"]
103
- end
104
-
105
- # Get snapshot type (swarm or workflow)
106
- #
107
- # @return [String] Snapshot type
108
- def type
109
- @data[:type] || @data["type"]
110
- end
111
-
112
- # Get timestamp when snapshot was created
113
- #
114
- # @return [String] ISO8601 timestamp
115
- def snapshot_at
116
- @data[:snapshot_at] || @data["snapshot_at"]
117
- end
118
-
119
- # Get SwarmSDK version that created this snapshot
120
- #
121
- # @return [String] SwarmSDK version
122
- def swarm_sdk_version
123
- @data[:swarm_sdk_version] || @data["swarm_sdk_version"]
124
- end
125
-
126
- # Get agent names from snapshot
127
- #
128
- # @return [Array<String>] Agent names
129
- def agent_names
130
- agents = @data[:agents] || @data["agents"]
131
- agents ? agents.keys.map(&:to_s) : []
132
- end
133
-
134
- # Get delegation instance names from snapshot
135
- #
136
- # @return [Array<String>] Delegation instance names
137
- def delegation_instance_names
138
- delegations = @data[:delegation_instances] || @data["delegation_instances"]
139
- delegations ? delegations.keys.map(&:to_s) : []
140
- end
141
-
142
- # Check if snapshot is for a swarm (vs workflow)
143
- #
144
- # @return [Boolean] true if swarm snapshot
145
- def swarm?
146
- type == "swarm"
147
- end
148
-
149
- # Check if snapshot is for a workflow
150
- #
151
- # @return [Boolean] true if workflow snapshot
152
- def workflow?
153
- type == "workflow"
154
- end
155
- end
156
- end