swarm_sdk 2.7.14 → 3.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) 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/edit.rb +111 -0
  42. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  43. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  44. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  45. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  46. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  47. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  48. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  49. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  50. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  51. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  52. data/lib/swarm_sdk/v3.rb +145 -0
  53. metadata +83 -148
  54. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  55. data/lib/swarm_sdk/agent/builder.rb +0 -705
  56. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  57. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  58. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  59. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  60. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  61. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  62. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  63. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  64. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  65. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  66. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  67. data/lib/swarm_sdk/agent/context.rb +0 -115
  68. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  69. data/lib/swarm_sdk/agent/definition.rb +0 -588
  70. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  71. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  72. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  73. data/lib/swarm_sdk/agent_registry.rb +0 -146
  74. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  75. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  76. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  77. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  78. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  79. data/lib/swarm_sdk/config.rb +0 -368
  80. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  81. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  82. data/lib/swarm_sdk/configuration.rb +0 -165
  83. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  84. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  85. data/lib/swarm_sdk/context_compactor.rb +0 -335
  86. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  87. data/lib/swarm_sdk/context_management/context.rb +0 -328
  88. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  89. data/lib/swarm_sdk/defaults.rb +0 -251
  90. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  91. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  92. data/lib/swarm_sdk/hooks/context.rb +0 -197
  93. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  94. data/lib/swarm_sdk/hooks/error.rb +0 -29
  95. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  96. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  97. data/lib/swarm_sdk/hooks/result.rb +0 -150
  98. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  99. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  100. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  101. data/lib/swarm_sdk/log_collector.rb +0 -227
  102. data/lib/swarm_sdk/log_stream.rb +0 -127
  103. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  104. data/lib/swarm_sdk/model_aliases.json +0 -8
  105. data/lib/swarm_sdk/models.json +0 -44002
  106. data/lib/swarm_sdk/models.rb +0 -161
  107. data/lib/swarm_sdk/node_context.rb +0 -245
  108. data/lib/swarm_sdk/observer/builder.rb +0 -81
  109. data/lib/swarm_sdk/observer/config.rb +0 -45
  110. data/lib/swarm_sdk/observer/manager.rb +0 -248
  111. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  112. data/lib/swarm_sdk/permissions/config.rb +0 -239
  113. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  114. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  115. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  116. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  117. data/lib/swarm_sdk/plugin.rb +0 -309
  118. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  119. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  120. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  121. data/lib/swarm_sdk/restore_result.rb +0 -65
  122. data/lib/swarm_sdk/result.rb +0 -241
  123. data/lib/swarm_sdk/snapshot.rb +0 -156
  124. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  125. data/lib/swarm_sdk/state_restorer.rb +0 -476
  126. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  127. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  128. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  129. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  130. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  131. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  132. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  133. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  134. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  135. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  136. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  137. data/lib/swarm_sdk/swarm.rb +0 -973
  138. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  139. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  140. data/lib/swarm_sdk/tools/base.rb +0 -63
  141. data/lib/swarm_sdk/tools/bash.rb +0 -280
  142. data/lib/swarm_sdk/tools/clock.rb +0 -46
  143. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  144. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  145. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  146. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  147. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  148. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  149. data/lib/swarm_sdk/tools/edit.rb +0 -145
  150. data/lib/swarm_sdk/tools/glob.rb +0 -166
  151. data/lib/swarm_sdk/tools/grep.rb +0 -235
  152. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  153. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  154. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  155. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  156. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  157. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  158. data/lib/swarm_sdk/tools/read.rb +0 -261
  159. data/lib/swarm_sdk/tools/registry.rb +0 -205
  160. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  161. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  163. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  164. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  165. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  166. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  167. data/lib/swarm_sdk/tools/think.rb +0 -100
  168. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  169. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  170. data/lib/swarm_sdk/tools/write.rb +0 -112
  171. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  172. data/lib/swarm_sdk/utils.rb +0 -68
  173. data/lib/swarm_sdk/validation_result.rb +0 -33
  174. data/lib/swarm_sdk/version.rb +0 -5
  175. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  176. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  177. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  178. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  179. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  180. data/lib/swarm_sdk/workflow.rb +0 -589
  181. data/lib/swarm_sdk.rb +0 -721
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ module Memory
6
+ # A self-contained memory unit (<=250 words)
7
+ #
8
+ # Cards are the atomic unit of the memory system. Each card captures
9
+ # a single idea, fact, decision, or concept with enough context to
10
+ # be understood in isolation.
11
+ #
12
+ # @example Create a fact card
13
+ # card = Card.new(
14
+ # text: "The API uses JWT tokens for authentication with RS256 signing.",
15
+ # type: :fact,
16
+ # entities: ["API", "JWT", "RS256"],
17
+ # source_turn_ids: ["turn_001"],
18
+ # )
19
+ class Card
20
+ TYPES = [:fact, :concept, :decision, :constraint, :preference, :incident].freeze
21
+
22
+ # @return [String] Unique card identifier (card_<hex>)
23
+ attr_reader :id
24
+
25
+ # @return [String] Card content (<=250 words, self-contained)
26
+ attr_accessor :text
27
+
28
+ # @return [Symbol] Card type (fact, concept, decision, constraint, preference, incident)
29
+ attr_reader :type
30
+
31
+ # @return [Array<String>] Named entities mentioned in the card
32
+ attr_accessor :entities
33
+
34
+ # @return [Array<String>] Turn IDs that contributed to this card
35
+ attr_accessor :source_turn_ids
36
+
37
+ # @return [Array<Float>, nil] Embedding vector (384 dimensions for MiniLM)
38
+ attr_accessor :embedding
39
+
40
+ # @return [Float] Importance score (0.0-1.0)
41
+ attr_accessor :importance
42
+
43
+ # @return [Float] Confidence score (0.0-1.0, default 1.0)
44
+ attr_accessor :confidence
45
+
46
+ # @return [Integer] Number of times this card was retrieved
47
+ attr_accessor :access_count
48
+
49
+ # @return [Time, nil] Last time this card was accessed
50
+ attr_accessor :last_accessed
51
+
52
+ # @return [Float] Dwell time weight (how long card was in context)
53
+ attr_accessor :dwell
54
+
55
+ # @return [Integer] Compression level (0=raw, 1=summary, 2=bullets, 3=schema, 4=embedding-only)
56
+ attr_accessor :compression_level
57
+
58
+ # @return [String, nil] ID of canonical card if this was merged
59
+ attr_accessor :canonical_id
60
+
61
+ # @return [Time] Creation timestamp
62
+ attr_reader :created_at
63
+
64
+ # @return [Time] Last update timestamp
65
+ attr_accessor :updated_at
66
+
67
+ # Create a new memory card
68
+ #
69
+ # @param text [String] Card content
70
+ # @param type [Symbol] Card type
71
+ # @param entities [Array<String>] Named entities
72
+ # @param source_turn_ids [Array<String>] Contributing turn IDs
73
+ # @param id [String, nil] Card ID (auto-generated if nil)
74
+ # @param embedding [Array<Float>, nil] Embedding vector
75
+ # @param importance [Float] Importance score
76
+ # @param confidence [Float] Confidence score (0.0-1.0)
77
+ # @param access_count [Integer] Access count
78
+ # @param last_accessed [Time, nil] Last access time
79
+ # @param dwell [Float] Dwell time weight
80
+ # @param compression_level [Integer] Compression level (0-4)
81
+ # @param canonical_id [String, nil] Canonical card ID
82
+ # @param created_at [Time, nil] Creation time
83
+ # @param updated_at [Time, nil] Update time
84
+ #
85
+ # @raise [ArgumentError] If type is invalid
86
+ def initialize(
87
+ text:,
88
+ type: :fact,
89
+ entities: [],
90
+ source_turn_ids: [],
91
+ id: nil,
92
+ embedding: nil,
93
+ importance: 0.5,
94
+ confidence: 1.0,
95
+ access_count: 0,
96
+ last_accessed: nil,
97
+ dwell: 0.0,
98
+ compression_level: 0,
99
+ canonical_id: nil,
100
+ created_at: nil,
101
+ updated_at: nil
102
+ )
103
+ @id = id || "card_#{SecureRandom.hex(6)}"
104
+ @text = text
105
+ @type = type.to_sym
106
+ @entities = Array(entities)
107
+ @source_turn_ids = Array(source_turn_ids)
108
+ @embedding = embedding
109
+ @importance = importance.to_f
110
+ @confidence = confidence.to_f
111
+ @access_count = access_count.to_i
112
+ @last_accessed = last_accessed
113
+ @dwell = dwell.to_f
114
+ @compression_level = compression_level.to_i
115
+ @canonical_id = canonical_id
116
+ @created_at = created_at || Time.now
117
+ @updated_at = updated_at || Time.now
118
+
119
+ validate!
120
+ end
121
+
122
+ # Record an access to this card (included in working context)
123
+ #
124
+ # Increments access count, updates last accessed time, and
125
+ # increases dwell weight to track how often this card has
126
+ # been included in the working context.
127
+ #
128
+ # @param dwell_increment [Float] Amount to add to dwell (default: 1.0)
129
+ # @return [void]
130
+ def record_access!(dwell_increment: 1.0)
131
+ @access_count += 1
132
+ @last_accessed = Time.now
133
+ @dwell += dwell_increment
134
+ @updated_at = Time.now
135
+ end
136
+
137
+ # Whether this card has been merged into a canonical card
138
+ #
139
+ # @return [Boolean]
140
+ def merged?
141
+ !@canonical_id.nil?
142
+ end
143
+
144
+ # Serialize to a hash for JSON storage
145
+ #
146
+ # @return [Hash] Serializable representation
147
+ def to_h
148
+ {
149
+ id: @id,
150
+ text: @text,
151
+ type: @type.to_s,
152
+ entities: @entities,
153
+ source_turn_ids: @source_turn_ids,
154
+ embedding: @embedding,
155
+ importance: @importance,
156
+ confidence: @confidence,
157
+ access_count: @access_count,
158
+ last_accessed: @last_accessed&.iso8601,
159
+ dwell: @dwell,
160
+ compression_level: @compression_level,
161
+ canonical_id: @canonical_id,
162
+ created_at: @created_at.iso8601,
163
+ updated_at: @updated_at.iso8601,
164
+ }
165
+ end
166
+
167
+ class << self
168
+ # Deserialize from a hash
169
+ #
170
+ # @param hash [Hash] Serialized card data
171
+ # @return [Card]
172
+ def from_h(hash)
173
+ hash = hash.transform_keys(&:to_sym)
174
+ new(
175
+ id: hash[:id],
176
+ text: hash[:text],
177
+ type: hash[:type]&.to_sym || :fact,
178
+ entities: hash[:entities] || [],
179
+ source_turn_ids: hash[:source_turn_ids] || [],
180
+ embedding: hash[:embedding],
181
+ importance: hash[:importance] || 0.5,
182
+ confidence: hash[:confidence] || 1.0,
183
+ access_count: hash[:access_count] || 0,
184
+ last_accessed: hash[:last_accessed] ? Time.parse(hash[:last_accessed]) : nil,
185
+ dwell: hash[:dwell] || 0.0,
186
+ compression_level: hash[:compression_level] || 0,
187
+ canonical_id: hash[:canonical_id],
188
+ created_at: hash[:created_at] ? Time.parse(hash[:created_at]) : nil,
189
+ updated_at: hash[:updated_at] ? Time.parse(hash[:updated_at]) : nil,
190
+ )
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ # @raise [ArgumentError] If type is invalid
197
+ def validate!
198
+ raise ArgumentError, "Invalid card type: #{@type}. Must be one of: #{TYPES.join(", ")}" unless TYPES.include?(@type)
199
+ raise ArgumentError, "Card text is required" if @text.nil? || @text.strip.empty?
200
+ raise ArgumentError, "Confidence must be 0.0-1.0" unless (0.0..1.0).cover?(@confidence)
201
+ raise ArgumentError, "Compression level must be 0-4" unless (0..4).cover?(@compression_level)
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ module Memory
6
+ # A topic cluster grouping related memory cards
7
+ #
8
+ # Clusters provide a higher-level organization of memory cards
9
+ # by topic. Each cluster maintains a rolling summary and tracks
10
+ # its member cards.
11
+ #
12
+ # @example Create a cluster
13
+ # cluster = Cluster.new(
14
+ # title: "Authentication System",
15
+ # rolling_summary: "The project uses JWT-based auth with RS256 signing...",
16
+ # card_ids: ["card_a1b2c3", "card_d4e5f6"],
17
+ # )
18
+ class Cluster
19
+ # @return [String] Unique cluster identifier
20
+ attr_reader :id
21
+
22
+ # @return [String] Cluster title/topic
23
+ attr_accessor :title
24
+
25
+ # @return [Array<Float>, nil] Cluster centroid embedding
26
+ attr_accessor :embedding
27
+
28
+ # @return [String] Rolling summary of cluster contents
29
+ attr_accessor :rolling_summary
30
+
31
+ # @return [Array<String>] Decision log entries
32
+ attr_accessor :decision_log
33
+
34
+ # @return [Array<String>] Key entities in this cluster
35
+ attr_accessor :key_entities
36
+
37
+ # @return [Array<String>] Member card IDs
38
+ attr_accessor :card_ids
39
+
40
+ # @return [Time] Creation timestamp
41
+ attr_reader :created_at
42
+
43
+ # @return [Time] Last update timestamp
44
+ attr_accessor :updated_at
45
+
46
+ # Create a new cluster
47
+ #
48
+ # @param title [String] Cluster title
49
+ # @param id [String, nil] Cluster ID (auto-generated if nil)
50
+ # @param embedding [Array<Float>, nil] Centroid embedding
51
+ # @param rolling_summary [String] Summary of contents
52
+ # @param decision_log [Array<String>] Decision entries
53
+ # @param key_entities [Array<String>] Key entities
54
+ # @param card_ids [Array<String>] Member card IDs
55
+ # @param created_at [Time, nil] Creation time
56
+ # @param updated_at [Time, nil] Update time
57
+ def initialize(
58
+ title:,
59
+ id: nil,
60
+ embedding: nil,
61
+ rolling_summary: "",
62
+ decision_log: [],
63
+ key_entities: [],
64
+ card_ids: [],
65
+ created_at: nil,
66
+ updated_at: nil
67
+ )
68
+ @id = id || "cluster_#{SecureRandom.hex(6)}"
69
+ @title = title
70
+ @embedding = embedding
71
+ @rolling_summary = rolling_summary
72
+ @decision_log = Array(decision_log)
73
+ @key_entities = Array(key_entities)
74
+ @card_ids = Array(card_ids)
75
+ @created_at = created_at || Time.now
76
+ @updated_at = updated_at || Time.now
77
+ end
78
+
79
+ # Add a card to this cluster
80
+ #
81
+ # @param card_id [String] Card ID to add
82
+ # @return [void]
83
+ def add_card(card_id)
84
+ return if @card_ids.include?(card_id)
85
+
86
+ @card_ids << card_id
87
+ @updated_at = Time.now
88
+ end
89
+
90
+ # Remove a card from this cluster
91
+ #
92
+ # @param card_id [String] Card ID to remove
93
+ # @return [void]
94
+ def remove_card(card_id)
95
+ @card_ids.delete(card_id)
96
+ @updated_at = Time.now
97
+ end
98
+
99
+ # Number of cards in this cluster
100
+ #
101
+ # @return [Integer]
102
+ def size
103
+ @card_ids.size
104
+ end
105
+
106
+ # Serialize to a hash
107
+ #
108
+ # @return [Hash]
109
+ def to_h
110
+ {
111
+ id: @id,
112
+ title: @title,
113
+ embedding: @embedding,
114
+ rolling_summary: @rolling_summary,
115
+ decision_log: @decision_log,
116
+ key_entities: @key_entities,
117
+ card_ids: @card_ids,
118
+ created_at: @created_at.iso8601,
119
+ updated_at: @updated_at.iso8601,
120
+ }
121
+ end
122
+
123
+ class << self
124
+ # Deserialize from a hash
125
+ #
126
+ # @param hash [Hash] Serialized cluster data
127
+ # @return [Cluster]
128
+ def from_h(hash)
129
+ hash = hash.transform_keys(&:to_sym)
130
+ new(
131
+ id: hash[:id],
132
+ title: hash[:title],
133
+ embedding: hash[:embedding],
134
+ rolling_summary: hash[:rolling_summary] || "",
135
+ decision_log: hash[:decision_log] || [],
136
+ key_entities: hash[:key_entities] || [],
137
+ card_ids: hash[:card_ids] || [],
138
+ created_at: hash[:created_at] ? Time.parse(hash[:created_at]) : nil,
139
+ updated_at: hash[:updated_at] ? Time.parse(hash[:updated_at]) : nil,
140
+ )
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end