swarm_memory 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. data/lib/swarm_memory.rb +7 -2
  4. metadata +6 -185
  5. data/lib/claude_swarm/base_executor.rb +0 -133
  6. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  7. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  8. data/lib/claude_swarm/cli.rb +0 -697
  9. data/lib/claude_swarm/commands/ps.rb +0 -215
  10. data/lib/claude_swarm/commands/show.rb +0 -139
  11. data/lib/claude_swarm/configuration.rb +0 -373
  12. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  13. data/lib/claude_swarm/json_handler.rb +0 -91
  14. data/lib/claude_swarm/mcp_generator.rb +0 -243
  15. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  16. data/lib/claude_swarm/openai/executor.rb +0 -256
  17. data/lib/claude_swarm/openai/responses.rb +0 -319
  18. data/lib/claude_swarm/orchestrator.rb +0 -878
  19. data/lib/claude_swarm/process_tracker.rb +0 -78
  20. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  21. data/lib/claude_swarm/session_path.rb +0 -42
  22. data/lib/claude_swarm/settings_generator.rb +0 -77
  23. data/lib/claude_swarm/system_utils.rb +0 -46
  24. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  25. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  27. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  28. data/lib/claude_swarm/version.rb +0 -5
  29. data/lib/claude_swarm/worktree_manager.rb +0 -475
  30. data/lib/claude_swarm/yaml_loader.rb +0 -22
  31. data/lib/claude_swarm.rb +0 -67
  32. data/lib/swarm_cli/cli.rb +0 -201
  33. data/lib/swarm_cli/command_registry.rb +0 -61
  34. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  35. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  36. data/lib/swarm_cli/commands/migrate.rb +0 -55
  37. data/lib/swarm_cli/commands/run.rb +0 -173
  38. data/lib/swarm_cli/config_loader.rb +0 -98
  39. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  40. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  41. data/lib/swarm_cli/interactive_repl.rb +0 -924
  42. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  43. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  44. data/lib/swarm_cli/migrate_options.rb +0 -54
  45. data/lib/swarm_cli/migrator.rb +0 -132
  46. data/lib/swarm_cli/options.rb +0 -151
  47. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  48. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  49. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  50. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  51. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  52. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  53. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  54. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  55. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  56. data/lib/swarm_cli/ui/icons.rb +0 -36
  57. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  58. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  59. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  60. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  61. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  62. data/lib/swarm_cli/version.rb +0 -5
  63. data/lib/swarm_cli.rb +0 -46
  64. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  65. data/lib/swarm_sdk/agent/builder.rb +0 -552
  66. data/lib/swarm_sdk/agent/chat.rb +0 -774
  67. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  68. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  69. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  70. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  71. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  72. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  73. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  75. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  76. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  77. data/lib/swarm_sdk/agent/context.rb +0 -116
  78. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  79. data/lib/swarm_sdk/agent/definition.rb +0 -477
  80. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  81. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  82. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  83. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  84. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  85. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  86. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  87. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  88. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  89. data/lib/swarm_sdk/configuration.rb +0 -135
  90. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  91. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  92. data/lib/swarm_sdk/context_compactor.rb +0 -335
  93. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  94. data/lib/swarm_sdk/context_management/context.rb +0 -328
  95. data/lib/swarm_sdk/defaults.rb +0 -196
  96. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  97. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  98. data/lib/swarm_sdk/hooks/context.rb +0 -197
  99. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  100. data/lib/swarm_sdk/hooks/error.rb +0 -29
  101. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  102. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  103. data/lib/swarm_sdk/hooks/result.rb +0 -150
  104. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  105. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  106. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  107. data/lib/swarm_sdk/log_collector.rb +0 -227
  108. data/lib/swarm_sdk/log_stream.rb +0 -127
  109. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  110. data/lib/swarm_sdk/model_aliases.json +0 -8
  111. data/lib/swarm_sdk/models.json +0 -1
  112. data/lib/swarm_sdk/models.rb +0 -120
  113. data/lib/swarm_sdk/node_context.rb +0 -245
  114. data/lib/swarm_sdk/observer/builder.rb +0 -81
  115. data/lib/swarm_sdk/observer/config.rb +0 -45
  116. data/lib/swarm_sdk/observer/manager.rb +0 -236
  117. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  118. data/lib/swarm_sdk/permissions/config.rb +0 -239
  119. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  120. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  121. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  122. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  123. data/lib/swarm_sdk/plugin.rb +0 -309
  124. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  125. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  126. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  127. data/lib/swarm_sdk/restore_result.rb +0 -65
  128. data/lib/swarm_sdk/result.rb +0 -123
  129. data/lib/swarm_sdk/snapshot.rb +0 -156
  130. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  131. data/lib/swarm_sdk/state_restorer.rb +0 -476
  132. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  133. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  134. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  135. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  136. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  137. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  138. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  139. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  140. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  141. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  142. data/lib/swarm_sdk/swarm.rb +0 -717
  143. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  144. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  145. data/lib/swarm_sdk/tools/bash.rb +0 -282
  146. data/lib/swarm_sdk/tools/clock.rb +0 -44
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  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 -163
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  160. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  161. data/lib/swarm_sdk/tools/read.rb +0 -261
  162. data/lib/swarm_sdk/tools/registry.rb +0 -205
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  166. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  167. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  168. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  169. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  170. data/lib/swarm_sdk/tools/think.rb +0 -98
  171. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  172. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  173. data/lib/swarm_sdk/tools/write.rb +0 -112
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  182. data/lib/swarm_sdk/workflow.rb +0 -554
  183. data/lib/swarm_sdk.rb +0 -524
  184. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -1,243 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ClaudeSwarm
4
- class McpGenerator
5
- def initialize(configuration, vibe: false, restore_session_path: nil)
6
- @config = configuration
7
- @vibe = vibe
8
- @restore_session_path = restore_session_path
9
- @session_path = nil # Will be set when needed
10
- @instance_ids = {} # Store instance IDs for all instances
11
- @restore_states = {} # Store loaded state data during restoration
12
- end
13
-
14
- def generate_all
15
- ensure_swarm_directory
16
-
17
- if @restore_session_path
18
- # Load existing instance IDs and states from state files
19
- load_instance_states
20
- else
21
- # Generate new instance IDs
22
- @config.instances.each_key do |name|
23
- @instance_ids[name] = "#{name}_#{SecureRandom.hex(4)}"
24
- end
25
- end
26
-
27
- @config.instances.each do |name, instance|
28
- generate_mcp_config(name, instance)
29
- end
30
- end
31
-
32
- def mcp_config_path(instance_name)
33
- File.join(session_path, "#{instance_name}.mcp.json")
34
- end
35
-
36
- private
37
-
38
- def session_path
39
- @session_path ||= SessionPath.from_env
40
- end
41
-
42
- def ensure_swarm_directory
43
- # Session directory is already created by orchestrator
44
- # Just ensure it exists
45
- SessionPath.ensure_directory(session_path)
46
- end
47
-
48
- def generate_mcp_config(name, instance)
49
- mcp_servers = {}
50
-
51
- # Add configured MCP servers
52
- instance[:mcps].each do |mcp|
53
- mcp_servers[mcp["name"]] = build_mcp_server_config(mcp)
54
- end
55
-
56
- # Add connection MCPs for other instances
57
- instance[:connections].each do |connection_name|
58
- connected_instance = @config.instances[connection_name]
59
- mcp_servers[connection_name] = build_instance_mcp_config(
60
- connection_name,
61
- connected_instance,
62
- calling_instance: name,
63
- calling_instance_id: @instance_ids[name],
64
- )
65
- end
66
-
67
- # Add Claude tools MCP server for OpenAI instances
68
- mcp_servers["claude_tools"] = build_claude_tools_mcp_config if instance[:provider] == "openai"
69
-
70
- config = {
71
- "instance_id" => @instance_ids[name],
72
- "instance_name" => name,
73
- "mcpServers" => mcp_servers,
74
- }
75
-
76
- JsonHandler.write_file!(mcp_config_path(name), config)
77
- end
78
-
79
- def build_mcp_server_config(mcp)
80
- case mcp["type"]
81
- when "stdio"
82
- {
83
- "type" => "stdio",
84
- "command" => mcp["command"],
85
- "args" => mcp["args"] || [],
86
- }.tap do |config|
87
- config["env"] = mcp["env"] if mcp["env"]
88
- end
89
- when "sse", "http"
90
- {
91
- "type" => mcp["type"],
92
- "url" => mcp["url"],
93
- }.tap do |config|
94
- config["headers"] = mcp["headers"] if mcp["headers"]
95
- end
96
- end
97
- end
98
-
99
- def build_claude_tools_mcp_config
100
- # Build environment for claude mcp serve by excluding Ruby/Bundler-specific variables
101
- # This preserves all system variables while removing Ruby contamination
102
- clean_env = ENV.to_h.reject do |key, _|
103
- key.start_with?("BUNDLE_") ||
104
- key.start_with?("RUBY") ||
105
- key.start_with?("GEM_") ||
106
- key == "RUBYOPT" ||
107
- key == "RUBYLIB"
108
- end
109
-
110
- {
111
- "type" => "stdio",
112
- "command" => "claude",
113
- "args" => ["mcp", "serve"],
114
- "env" => clean_env,
115
- }
116
- end
117
-
118
- def build_instance_mcp_config(name, instance, calling_instance:, calling_instance_id:)
119
- # Get the path to the claude-swarm executable
120
- exe_path = "claude-swarm"
121
-
122
- # Build command-line arguments for Thor
123
- args = [
124
- "mcp-serve",
125
- "--name",
126
- name,
127
- "--directory",
128
- instance[:directory],
129
- "--model",
130
- instance[:model],
131
- ]
132
-
133
- # Add directories array if we have multiple directories
134
- args.push("--directories", *instance[:directories]) if instance[:directories] && instance[:directories].size > 1
135
-
136
- # Add optional arguments
137
- # Handle prompt_file by reading the file contents
138
- if instance[:prompt_file]
139
- prompt_file_path = File.join(@config.root_directory, instance[:prompt_file])
140
- if File.exist?(prompt_file_path)
141
- prompt_content = File.read(prompt_file_path)
142
- args.push("--prompt", prompt_content)
143
- end
144
- elsif instance[:prompt]
145
- args.push("--prompt", instance[:prompt])
146
- end
147
-
148
- args.push("--description", instance[:description]) if instance[:description]
149
-
150
- args.push("--allowed-tools", instance[:allowed_tools].join(",")) if instance[:allowed_tools] && !instance[:allowed_tools].empty?
151
-
152
- args.push("--disallowed-tools", instance[:disallowed_tools].join(",")) if instance[:disallowed_tools] && !instance[:disallowed_tools].empty?
153
-
154
- args.push("--connections", instance[:connections].join(",")) if instance[:connections] && !instance[:connections].empty?
155
-
156
- args.push("--mcp-config-path", mcp_config_path(name))
157
-
158
- args.push("--calling-instance", calling_instance) if calling_instance
159
-
160
- args.push("--calling-instance-id", calling_instance_id) if calling_instance_id
161
-
162
- args.push("--instance-id", @instance_ids[name]) if @instance_ids[name]
163
-
164
- args.push("--vibe") if @vibe || instance[:vibe]
165
-
166
- # Add provider-specific parameters
167
- if instance[:provider]
168
- args.push("--provider", instance[:provider])
169
-
170
- # Add OpenAI-specific parameters
171
- if instance[:provider] == "openai"
172
- args.push("--reasoning-effort", instance[:reasoning_effort]) if instance[:reasoning_effort]
173
- args.push("--temperature", instance[:temperature].to_s) if instance[:temperature]
174
- args.push("--api-version", instance[:api_version]) if instance[:api_version]
175
- args.push("--openai-token-env", instance[:openai_token_env]) if instance[:openai_token_env]
176
- args.push("--base-url", instance[:base_url]) if instance[:base_url]
177
- args.push("--zdr", instance[:zdr].to_s) if instance.key?(:zdr)
178
- end
179
- end
180
-
181
- # Add claude session ID if restoring
182
- if @restore_states[name.to_s]
183
- claude_session_id = @restore_states[name.to_s]["claude_session_id"]
184
- args.push("--claude-session-id", claude_session_id) if claude_session_id
185
- end
186
-
187
- # Capture environment variables needed for running claude-swarm
188
- # We intentionally exclude Bundler variables to ensure we use the system-installed gem
189
- required_env = {}
190
-
191
- # Claude Swarm-specific variables (always needed)
192
- ENV.each do |k, v|
193
- required_env[k] = v if k.start_with?("CLAUDE_SWARM_")
194
- end
195
-
196
- # Ruby-specific variables that MCP servers need
197
- # Exclude RUBYOPT and RUBYLIB to avoid Bundler interference
198
- [
199
- "RUBY_ROOT",
200
- "RUBY_ENGINE",
201
- "RUBY_VERSION",
202
- "GEM_ROOT",
203
- "GEM_HOME",
204
- "GEM_PATH",
205
- "PATH",
206
- ].each do |key|
207
- required_env[key] = ENV[key] if ENV[key]
208
- end
209
-
210
- config = {
211
- "type" => "stdio",
212
- "command" => exe_path,
213
- "args" => args,
214
- }
215
-
216
- # Add required environment variables if any exist
217
- config["env"] = required_env unless required_env.empty?
218
-
219
- config
220
- end
221
-
222
- def load_instance_states
223
- state_dir = File.join(@restore_session_path, "state")
224
- return unless Dir.exist?(state_dir)
225
-
226
- Dir.glob(File.join(state_dir, "*.json")).each do |state_file|
227
- data = JsonHandler.parse_file!(state_file)
228
- instance_name = data["instance_name"]
229
- instance_id = data["instance_id"]
230
-
231
- # Check both string and symbol keys since config instances might have either
232
- if instance_name && (@config.instances.key?(instance_name) || @config.instances.key?(instance_name.to_sym))
233
- # Store with the same key type as in @config.instances
234
- key = @config.instances.key?(instance_name) ? instance_name : instance_name.to_sym
235
- @instance_ids[key] = instance_id
236
- @restore_states[instance_name] = data
237
- end
238
- rescue StandardError
239
- # Skip invalid state files
240
- end
241
- end
242
- end
243
- end
@@ -1,256 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ClaudeSwarm
4
- module OpenAI
5
- class ChatCompletion
6
- MAX_TURNS_WITH_TOOLS = 100_000 # virtually infinite
7
-
8
- def initialize(openai_client:, mcp_client:, available_tools:, executor:, instance_name:, model:, temperature: nil, reasoning_effort: nil, zdr: false)
9
- @openai_client = openai_client
10
- @mcp_client = mcp_client
11
- @available_tools = available_tools
12
- @executor = executor
13
- @instance_name = instance_name
14
- @model = model
15
- @temperature = temperature
16
- @reasoning_effort = reasoning_effort
17
- @zdr = zdr # Not used in chat_completion API, but kept for compatibility
18
- @conversation_messages = []
19
- end
20
-
21
- def execute(prompt, options = {})
22
- # Build messages array
23
- messages = build_messages(prompt, options)
24
-
25
- # Process chat with recursive tool handling
26
- result = process_chat_completion(messages)
27
-
28
- # Update conversation state
29
- @conversation_messages = messages
30
-
31
- result
32
- end
33
-
34
- def reset_session
35
- @conversation_messages = []
36
- end
37
-
38
- private
39
-
40
- def build_messages(prompt, options)
41
- messages = []
42
-
43
- # Add system prompt if provided
44
- system_prompt = options[:system_prompt]
45
- if system_prompt && @conversation_messages.empty?
46
- messages << { role: "system", content: system_prompt }
47
- elsif !@conversation_messages.empty?
48
- # Use existing conversation
49
- messages = @conversation_messages.dup
50
- end
51
-
52
- # Add user message
53
- messages << { role: "user", content: prompt }
54
-
55
- messages
56
- end
57
-
58
- def process_chat_completion(messages, depth = 0)
59
- # Prevent infinite recursion
60
- if depth > MAX_TURNS_WITH_TOOLS
61
- @executor.logger.error { "Maximum recursion depth reached in tool execution" }
62
- return "Error: Maximum tool call depth exceeded"
63
- end
64
-
65
- # Build parameters
66
- parameters = {
67
- model: @model,
68
- messages: messages,
69
- }
70
-
71
- parameters[:temperature] = @temperature if @temperature
72
- parameters[:reasoning_effort] = @reasoning_effort if @reasoning_effort
73
-
74
- # Add tools if available
75
- parameters[:tools] = @mcp_client.to_openai_tools if @available_tools&.any? && @mcp_client
76
-
77
- # Log the request parameters
78
- @executor.logger.info { "Chat API Request (depth=#{depth}): #{JsonHandler.pretty_generate!(parameters)}" }
79
-
80
- # Append to session JSON
81
- append_to_session_json({
82
- type: "openai_request",
83
- api: "chat",
84
- depth: depth,
85
- parameters: parameters,
86
- })
87
-
88
- # Make the API call without streaming
89
- begin
90
- response = @openai_client.chat(parameters: parameters)
91
- rescue StandardError => e
92
- @executor.logger.error { "Chat API error: #{e.class} - #{e.message}" }
93
- @executor.logger.error { "Request parameters: #{JsonHandler.pretty_generate!(parameters)}" }
94
-
95
- # Try to extract and log the response body for better debugging
96
- if e.respond_to?(:response)
97
- begin
98
- error_body = e.response[:body]
99
- @executor.logger.error { "Error response body: #{error_body}" }
100
- rescue StandardError => parse_error
101
- @executor.logger.error { "Could not parse error response: #{parse_error.message}" }
102
- end
103
- end
104
-
105
- # Log error to session JSON
106
- append_to_session_json({
107
- type: "openai_error",
108
- api: "chat",
109
- depth: depth,
110
- error: {
111
- class: e.class.to_s,
112
- message: e.message,
113
- response_body: e.respond_to?(:response) ? e.response[:body] : nil,
114
- backtrace: e.backtrace.first(5),
115
- },
116
- })
117
-
118
- return "Error calling OpenAI chat API: #{e.message}"
119
- end
120
-
121
- # Log the response
122
- @executor.logger.info { "Chat API Response (depth=#{depth}): #{JsonHandler.pretty_generate!(response)}" }
123
-
124
- # Append to session JSON
125
- append_to_session_json({
126
- type: "openai_response",
127
- api: "chat",
128
- depth: depth,
129
- response: response,
130
- })
131
-
132
- # Extract the message from the response
133
- message = response.dig("choices", 0, "message")
134
-
135
- if message.nil?
136
- @executor.logger.error { "No message in response: #{response.inspect}" }
137
- return "Error: No response from OpenAI"
138
- end
139
-
140
- # Check if there are tool calls
141
- if message["tool_calls"]
142
- # Add the assistant message with tool calls
143
- messages << {
144
- role: "assistant",
145
- content: nil,
146
- tool_calls: message["tool_calls"],
147
- }
148
-
149
- # Execute tools and collect results
150
- execute_and_append_tool_results(message["tool_calls"], messages)
151
-
152
- # Recursively process the next response
153
- process_chat_completion(messages, depth + 1)
154
- else
155
- # Regular text response - this is the final response
156
- response_text = message["content"] || ""
157
- messages << { role: "assistant", content: response_text }
158
- response_text
159
- end
160
- end
161
-
162
- def execute_and_append_tool_results(tool_calls, messages)
163
- # Log tool calls
164
- @executor.logger.info { "Executing tool calls: #{JsonHandler.pretty_generate!(tool_calls)}" }
165
-
166
- # Append to session JSON
167
- append_to_session_json({
168
- type: "tool_calls",
169
- api: "chat",
170
- tool_calls: tool_calls,
171
- })
172
-
173
- # Execute tool calls in parallel threads
174
- threads = tool_calls.map do |tool_call|
175
- Thread.new do
176
- tool_name = tool_call.dig("function", "name")
177
- tool_args_str = tool_call.dig("function", "arguments")
178
-
179
- begin
180
- # Parse arguments
181
- tool_args = tool_args_str.is_a?(String) ? JsonHandler.parse!(tool_args_str) : tool_args_str
182
-
183
- # Log tool execution
184
- @executor.logger.info { "Executing tool: #{tool_name} with args: #{JsonHandler.pretty_generate!(tool_args)}" }
185
-
186
- # Execute tool via MCP
187
- result = @mcp_client.call_tool(tool_name, tool_args)
188
-
189
- # Log result
190
- @executor.logger.info { "Tool result for #{tool_name}: #{result}" }
191
-
192
- # Append to session JSON
193
- append_to_session_json({
194
- type: "tool_execution",
195
- tool_name: tool_name,
196
- arguments: tool_args,
197
- result: result.to_s,
198
- })
199
-
200
- # Return success result
201
- {
202
- success: true,
203
- tool_call_id: tool_call["id"],
204
- role: "tool",
205
- name: tool_name,
206
- content: result.to_s,
207
- }
208
- rescue StandardError => e
209
- @executor.logger.error { "Tool execution failed for #{tool_name}: #{e.message}" }
210
- @executor.logger.error { e.backtrace.join("\n") }
211
-
212
- # Append error to session JSON
213
- append_to_session_json({
214
- type: "tool_error",
215
- tool_name: tool_name,
216
- arguments: tool_args,
217
- error: {
218
- class: e.class.to_s,
219
- message: e.message,
220
- backtrace: e.backtrace.first(5),
221
- },
222
- })
223
-
224
- # Return error result
225
- {
226
- success: false,
227
- tool_call_id: tool_call["id"],
228
- role: "tool",
229
- name: tool_name,
230
- content: "Error: #{e.message}",
231
- }
232
- end
233
- end
234
- end
235
-
236
- # Collect results from all threads
237
- tool_results = threads.map(&:value)
238
-
239
- # Add all tool results to messages
240
- tool_results.each do |result|
241
- messages << {
242
- tool_call_id: result[:tool_call_id],
243
- role: result[:role],
244
- name: result[:name],
245
- content: result[:content],
246
- }
247
- end
248
- end
249
-
250
- def append_to_session_json(event)
251
- # Delegate to the executor's log method
252
- @executor.log(event) if @executor.respond_to?(:log)
253
- end
254
- end
255
- end
256
- end