swarm_sdk 2.0.1 → 2.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c45fe12dc7f0cc16c5e82d539f941d22cfc847919f108000b73a6ff259819d24
4
- data.tar.gz: cd18d30692c2686c60c3e20e48081ecab97ab81f7bc9051da8bb9a3bf862a979
3
+ metadata.gz: 9aba64a75c41f9957bb3e11afbe0dd5a5fc6422793fda5d3fb829ac0042cbe18
4
+ data.tar.gz: 4f403fb7d107fa712a8b847775fea1140a03904cf066b8e848b914f4b3c573e3
5
5
  SHA512:
6
- metadata.gz: 9c1d02a1552463ab920c2e851a47449e6ed6eec82cd8874781cbc37e526937094a0d4b4625ae68596abb1825224fd7021c918f68f32f9d5d900406bf381c883e
7
- data.tar.gz: 85ba1af56c5fa6726565a8b65a43a5268aa01ee9381e1c12afc0d0ae75544f82a689bf2939b4b0a9213046c3e8efda84fa83c30a2f06f5bcbc4cd3c1b43fbbeb
6
+ metadata.gz: 204b9d889968b2d582557b06169d02828abd4c365b00e2a6551cc7f4c44a2ec7ae452a55d1e25b59fdc8a0b10c486c365cce2b2332d02f0d54b502ec6667af75
7
+ data.tar.gz: ae46da01755ef0c98b162c7c2be3c51ba8831dbbdd10301f937e0eed0260f9694b7514658d437e9d65d9cc58e06d7a877600ec29fd48eb773eec8514d74440b1
@@ -44,7 +44,7 @@ module SwarmSDK
44
44
  @headers = {}
45
45
  @timeout = nil
46
46
  @mcp_servers = []
47
- @include_default_tools = true
47
+ @disable_default_tools = nil # nil = include all default tools
48
48
  @bypass_permissions = false
49
49
  @coding_agent = nil # nil = not set (will default to false in Definition)
50
50
  @assume_model_exists = nil
@@ -124,9 +124,19 @@ module SwarmSDK
124
124
  @mcp_servers << server_config
125
125
  end
126
126
 
127
- # Set include_default_tools flag (deprecated - use tools method with include_default parameter)
128
- def include_default_tools(enabled)
129
- @include_default_tools = enabled
127
+ # Disable default tools
128
+ #
129
+ # @param value [Boolean, Array<Symbol>]
130
+ # - true: Disable ALL default tools
131
+ # - Array of symbols: Disable specific tools (e.g., [:Think, :TodoWrite])
132
+ #
133
+ # @example Disable all default tools
134
+ # disable_default_tools true
135
+ #
136
+ # @example Disable specific tools
137
+ # disable_default_tools [:Think, :TodoWrite]
138
+ def disable_default_tools(value)
139
+ @disable_default_tools = value
130
140
  end
131
141
 
132
142
  # Set bypass_permissions flag
@@ -163,13 +173,14 @@ module SwarmSDK
163
173
  @description = text
164
174
  end
165
175
 
166
- # Add tools
176
+ # Set or add tools
167
177
  #
168
178
  # Uses Set internally to automatically deduplicate tool names across multiple calls.
169
179
  # This allows calling tools() multiple times without worrying about duplicates.
170
180
  #
171
181
  # @param tool_names [Array<Symbol>] Tool names to add
172
182
  # @param include_default [Boolean] Whether to include default tools (Read, Grep, etc.)
183
+ # @param replace [Boolean] If true, replaces existing tools instead of merging (default: false)
173
184
  #
174
185
  # @example Basic usage with defaults
175
186
  # tools :Grep, :Read # include_default: true is implicit
@@ -181,9 +192,14 @@ module SwarmSDK
181
192
  # tools :Read
182
193
  # tools :Write, :Edit # @tools now contains Set[:Read, :Write, :Edit]
183
194
  # tools :Read # Still Set[:Read, :Write, :Edit] - no duplicate
184
- def tools(*tool_names, include_default: true)
195
+ #
196
+ # @example Replace tools (for markdown overrides)
197
+ # tools :Read, :Write, replace: true # Replaces all existing tools
198
+ def tools(*tool_names, include_default: true, replace: false)
199
+ @tools = Set.new if replace
185
200
  @tools.merge(tool_names.map(&:to_sym))
186
- @include_default_tools = include_default
201
+ # When include_default is false, disable all default tools
202
+ @disable_default_tools = true unless include_default
187
203
  end
188
204
 
189
205
  # Add tools from all_agents configuration
@@ -337,7 +353,7 @@ module SwarmSDK
337
353
  agent_config[:headers] = @headers if @headers.any?
338
354
  agent_config[:timeout] = @timeout if @timeout
339
355
  agent_config[:mcp_servers] = @mcp_servers if @mcp_servers.any?
340
- agent_config[:include_default_tools] = @include_default_tools
356
+ agent_config[:disable_default_tools] = @disable_default_tools unless @disable_default_tools.nil?
341
357
  agent_config[:bypass_permissions] = @bypass_permissions
342
358
  agent_config[:coding_agent] = @coding_agent
343
359
  agent_config[:assume_model_exists] = @assume_model_exists unless @assume_model_exists.nil?
@@ -38,7 +38,7 @@ module SwarmSDK
38
38
  :parameters,
39
39
  :headers,
40
40
  :timeout,
41
- :include_default_tools,
41
+ :disable_default_tools,
42
42
  :coding_agent,
43
43
  :default_permissions,
44
44
  :agent_permissions,
@@ -73,15 +73,15 @@ module SwarmSDK
73
73
  @timeout = config[:timeout] || DEFAULT_TIMEOUT
74
74
  @bypass_permissions = config[:bypass_permissions] || false
75
75
  @max_concurrent_tools = config[:max_concurrent_tools]
76
- # Default to true when base_url is set, false otherwise (unless explicitly specified)
77
- @assume_model_exists = if config.key?(:assume_model_exists)
78
- config[:assume_model_exists]
79
- else
80
- (base_url ? true : false)
81
- end
76
+ # Always assume model exists - SwarmSDK validates models separately using models.json
77
+ # This prevents RubyLLM from trying to validate models in its registry
78
+ @assume_model_exists = true
82
79
 
83
- # include_default_tools defaults to true if not specified
84
- @include_default_tools = config.key?(:include_default_tools) ? config[:include_default_tools] : true
80
+ # disable_default_tools can be:
81
+ # - nil/not set: include all default tools (default behavior)
82
+ # - true: disable ALL default tools
83
+ # - Array of symbols: disable specific tools (e.g., [:Think, :TodoWrite])
84
+ @disable_default_tools = config[:disable_default_tools]
85
85
 
86
86
  # coding_agent defaults to false if not specified
87
87
  # When true, includes the base system prompt for coding tasks
@@ -120,7 +120,7 @@ module SwarmSDK
120
120
  {
121
121
  name: @name,
122
122
  description: @description,
123
- model: @model,
123
+ model: SwarmSDK::Models.resolve_alias(@model), # Resolve model aliases
124
124
  directory: @directory,
125
125
  tools: @tools,
126
126
  delegates_to: @delegates_to,
@@ -133,7 +133,7 @@ module SwarmSDK
133
133
  headers: @headers,
134
134
  timeout: @timeout,
135
135
  bypass_permissions: @bypass_permissions,
136
- include_default_tools: @include_default_tools,
136
+ disable_default_tools: @disable_default_tools,
137
137
  coding_agent: @coding_agent,
138
138
  assume_model_exists: @assume_model_exists,
139
139
  max_concurrent_tools: @max_concurrent_tools,
@@ -168,45 +168,53 @@ module SwarmSDK
168
168
 
169
169
  private
170
170
 
171
- # Validate that model exists in RubyLLM registry
171
+ # Validate that model exists in SwarmSDK's model registry
172
+ #
173
+ # Uses SwarmSDK's static models.json instead of RubyLLM's dynamic registry.
174
+ # This provides stable, offline model validation without network calls.
175
+ #
176
+ # Process:
177
+ # 1. Try to find model directly in models.json
178
+ # 2. If not found, try to resolve as alias and find again
179
+ # 3. If still not found, return warning with suggestions
172
180
  #
173
181
  # @return [Hash, nil] Warning hash if model not found, nil otherwise
174
182
  def validate_model
175
- # Try to find model in registry (searches all providers)
176
- RubyLLM.models.find(@model)
177
- nil # Model exists
183
+ # Try direct lookup first
184
+ model_data = SwarmSDK::Models.all.find { |m| (m["id"] || m[:id]) == @model }
185
+
186
+ # If not found, try alias resolution
187
+ unless model_data
188
+ resolved_id = SwarmSDK::Models.resolve_alias(@model)
189
+ # Only search again if alias was different
190
+ if resolved_id != @model
191
+ model_data = SwarmSDK::Models.all.find { |m| (m["id"] || m[:id]) == resolved_id }
192
+ end
193
+ end
194
+
195
+ if model_data
196
+ nil # Model exists (either directly or via alias)
197
+ else
198
+ # Model not found - return warning with suggestions
199
+ {
200
+ type: :model_not_found,
201
+ agent: @name,
202
+ model: @model,
203
+ error_message: "Unknown model: #{@model}",
204
+ suggestions: SwarmSDK::Models.suggest_similar(@model),
205
+ }
206
+ end
178
207
  rescue StandardError => e
179
- # Model not found - return warning with suggestions
208
+ # Return warning on error
180
209
  {
181
210
  type: :model_not_found,
182
211
  agent: @name,
183
212
  model: @model,
184
213
  error_message: e.message,
185
- suggestions: suggest_similar_models,
214
+ suggestions: [],
186
215
  }
187
216
  end
188
217
 
189
- # Suggest similar models when a model is not found
190
- #
191
- # @return [Array<Hash>] Up to 3 similar models with their info
192
- def suggest_similar_models
193
- normalized_query = @model.to_s.downcase.gsub(/[.\-_]/, "")
194
-
195
- RubyLLM.models.all.select do |model_info|
196
- normalized_id = model_info.id.downcase.gsub(/[.\-_]/, "")
197
- normalized_id.include?(normalized_query) ||
198
- model_info.name&.downcase&.gsub(/[.\-_]/, "")&.include?(normalized_query)
199
- end.first(3).map do |model_info|
200
- {
201
- id: model_info.id,
202
- name: model_info.name,
203
- context_window: model_info.context_window,
204
- }
205
- end
206
- rescue StandardError
207
- []
208
- end
209
-
210
218
  def build_full_system_prompt(custom_prompt)
211
219
  # If coding_agent is false (default), return custom prompt with optional TODO/Scratchpad info
212
220
  # If coding_agent is true, include full base prompt for coding tasks
@@ -219,7 +227,7 @@ module SwarmSDK
219
227
  else
220
228
  rendered_base
221
229
  end
222
- elsif @include_default_tools
230
+ elsif default_tools_enabled?
223
231
  # Non-coding agent: optionally include TODO/Scratchpad sections if default tools available
224
232
  non_coding_base = render_non_coding_base_prompt
225
233
 
@@ -237,6 +245,13 @@ module SwarmSDK
237
245
  end
238
246
  end
239
247
 
248
+ # Check if default tools are enabled (i.e., not disabled)
249
+ #
250
+ # @return [Boolean] True if default tools should be included
251
+ def default_tools_enabled?
252
+ @disable_default_tools != true
253
+ end
254
+
240
255
  def render_base_system_prompt
241
256
  cwd = @directory || Dir.pwd
242
257
  platform = RUBY_PLATFORM
@@ -253,9 +268,27 @@ module SwarmSDK
253
268
 
254
269
  def render_non_coding_base_prompt
255
270
  # Simplified base prompt for non-coding agents
256
- # Only includes TODO and Scratchpad tool information
271
+ # Includes environment info, TODO, and Scratchpad tool information
257
272
  # Does not steer towards coding tasks
273
+ cwd = @directory || Dir.pwd
274
+ platform = RUBY_PLATFORM
275
+ os_version = begin
276
+ %x(uname -sr 2>/dev/null).strip
277
+ rescue
278
+ RUBY_PLATFORM
279
+ end
280
+ date = Time.now.strftime("%Y-%m-%d")
281
+
258
282
  <<~PROMPT.strip
283
+ # Environment
284
+
285
+ <env>
286
+ Working directory: #{cwd}
287
+ Platform: #{platform}
288
+ OS Version: #{os_version}
289
+ Today's date: #{date}
290
+ </env>
291
+
259
292
  # Task Management
260
293
 
261
294
  You have access to the TodoWrite tool to help you manage and plan tasks. Use this tool to track your progress and give visibility into your work.
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ # Adapter for converting Claude Code agent markdown files to SwarmSDK format
5
+ #
6
+ # Claude Code agent files use a different syntax and conventions than SwarmSDK:
7
+ # - Tools are comma-separated strings instead of arrays
8
+ # - Model shortcuts like 'sonnet', 'opus', 'haiku' instead of full model IDs
9
+ # - Tool permissions like 'Write(src/**)' instead of SwarmSDK's permission system
10
+ # - Required 'name' field in frontmatter
11
+ #
12
+ # This adapter:
13
+ # - Detects Claude Code format by checking frontmatter markers
14
+ # - Converts tools from comma-separated strings to arrays
15
+ # - Maps model shortcuts to canonical model IDs
16
+ # - Strips unsupported tool permission syntax with warnings
17
+ # - Sets coding_agent: true by default
18
+ # - Warns about unsupported fields
19
+ #
20
+ # @example Parse a Claude Code agent file
21
+ # content = File.read('.claude/agents/reviewer.md')
22
+ # config = ClaudeCodeAgentAdapter.parse(content, :reviewer)
23
+ # agent = Agent::Definition.new(:reviewer, config)
24
+ #
25
+ class ClaudeCodeAgentAdapter
26
+ # Fields supported in Claude Code agent frontmatter
27
+ SUPPORTED_FIELDS = ["name", "description", "tools", "model"].freeze
28
+
29
+ # SwarmSDK documentation URL for reference
30
+ SWARM_SDK_DOCS_URL = "https://github.com/parruda/claude-swarm/blob/main/docs/v2/README.md"
31
+
32
+ # Pattern to detect tool permission syntax like Write(src/**)
33
+ TOOL_PERMISSION_PATTERN = /^([A-Za-z_]+)\([^)]+\)$/
34
+
35
+ class << self
36
+ # Detect if content appears to be in Claude Code agent format
37
+ #
38
+ # Detection is based on tools field type:
39
+ # - Claude Code: tools is a comma-separated string (e.g., "Read, Write, Bash")
40
+ # - SwarmSDK: tools is an array (e.g., [Read, Write, Bash])
41
+ #
42
+ # Note: The 'name' field alone is not sufficient since SwarmSDK also supports it
43
+ #
44
+ # @param content [String] Markdown content with YAML frontmatter
45
+ # @return [Boolean] true if content appears to be Claude Code format
46
+ def claude_code_format?(content)
47
+ return false unless content =~ /\A---\s*\n(.*?)\n---\s*\n/m
48
+
49
+ frontmatter_yaml = Regexp.last_match(1)
50
+ frontmatter = YAML.safe_load(frontmatter_yaml, permitted_classes: [Symbol], aliases: true)
51
+
52
+ return false unless frontmatter.is_a?(Hash)
53
+
54
+ # Only detect as Claude Code if tools field is a comma-separated string
55
+ # This is the most reliable indicator since SwarmSDK always uses arrays
56
+ frontmatter.key?("tools") && frontmatter["tools"].is_a?(String)
57
+ rescue Psych::SyntaxError
58
+ false
59
+ end
60
+
61
+ # Parse Claude Code agent markdown and convert to SwarmSDK format
62
+ #
63
+ # @param content [String] Markdown content with YAML frontmatter
64
+ # @param agent_name [Symbol, String] Name of the agent
65
+ # @param inherit_model [String, nil] Model to use when frontmatter has 'inherit'
66
+ # @return [Hash] Configuration hash suitable for Agent::Definition.new
67
+ # @raise [ConfigurationError] if content format is invalid
68
+ def parse(content, agent_name, inherit_model: nil)
69
+ new(inherit_model: inherit_model).parse(content, agent_name)
70
+ end
71
+ end
72
+
73
+ # Initialize adapter with optional context
74
+ #
75
+ # @param inherit_model [String, nil] Model to use when frontmatter has 'inherit'
76
+ def initialize(inherit_model: nil)
77
+ @inherit_model = inherit_model
78
+ @warnings = []
79
+ end
80
+
81
+ # Parse Claude Code agent content
82
+ #
83
+ # @param content [String] Markdown content with YAML frontmatter
84
+ # @param agent_name [Symbol, String] Name of the agent
85
+ # @return [Hash] Configuration hash for Agent::Definition
86
+ # @raise [ConfigurationError] if format is invalid
87
+ def parse(content, agent_name)
88
+ unless content =~ /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
89
+ raise ConfigurationError, "Invalid Claude Code agent format. Expected YAML frontmatter followed by prompt content."
90
+ end
91
+
92
+ frontmatter_yaml = Regexp.last_match(1)
93
+ prompt_content = Regexp.last_match(2).strip
94
+
95
+ frontmatter = YAML.safe_load(frontmatter_yaml, permitted_classes: [Symbol], aliases: true)
96
+
97
+ unless frontmatter.is_a?(Hash)
98
+ raise ConfigurationError, "Invalid frontmatter format in Claude Code agent file"
99
+ end
100
+
101
+ config = build_config(frontmatter, prompt_content, agent_name)
102
+ emit_warnings(agent_name)
103
+ config
104
+ end
105
+
106
+ private
107
+
108
+ # Build SwarmSDK configuration from Claude Code frontmatter
109
+ def build_config(frontmatter, prompt_content, agent_name)
110
+ warn_unknown_fields(frontmatter)
111
+
112
+ config = {
113
+ description: frontmatter["description"],
114
+ system_prompt: prompt_content,
115
+ coding_agent: true, # Default for Claude Code agents
116
+ }
117
+
118
+ # Parse tools if present
119
+ if frontmatter["tools"]
120
+ config[:tools] = parse_tools(frontmatter["tools"])
121
+ end
122
+
123
+ # Parse model if present
124
+ if frontmatter["model"]
125
+ config[:model] = resolve_model(frontmatter["model"])
126
+ end
127
+
128
+ config
129
+ end
130
+
131
+ # Parse tools field - handles both comma-separated string and array
132
+ #
133
+ # @param tools_field [String, Array] Tools from frontmatter
134
+ # @return [Array<String>] Array of tool names
135
+ def parse_tools(tools_field)
136
+ tools_array = if tools_field.is_a?(String)
137
+ tools_field.split(",").map(&:strip)
138
+ else
139
+ Array(tools_field).map(&:to_s)
140
+ end
141
+
142
+ # Clean tool permissions and collect warnings
143
+ tools_array.map { |tool| clean_tool_permissions(tool) }.compact
144
+ end
145
+
146
+ # Strip tool permission syntax and warn if detected
147
+ #
148
+ # @param tool_string [String] Tool name, possibly with permissions like 'Write(src/**)'
149
+ # @return [String, nil] Clean tool name, or nil if invalid
150
+ def clean_tool_permissions(tool_string)
151
+ if tool_string =~ TOOL_PERMISSION_PATTERN
152
+ tool_name = Regexp.last_match(1)
153
+ @warnings << "Tool permission syntax '#{tool_string}' detected in agent file. SwarmSDK supports permissions but uses different syntax. Using '#{tool_name}' without restrictions for now. See SwarmSDK documentation for permission configuration: #{SWARM_SDK_DOCS_URL}"
154
+ tool_name
155
+ else
156
+ tool_string
157
+ end
158
+ end
159
+
160
+ # Resolve model shortcuts to canonical model IDs
161
+ #
162
+ # Uses SwarmSDK::Models.resolve_alias to map shortcuts like 'sonnet'
163
+ # to the latest model IDs from model_aliases.json.
164
+ #
165
+ # @param model_field [String] Model from frontmatter
166
+ # @return [String, Symbol] Canonical model ID or :inherit symbol
167
+ def resolve_model(model_field)
168
+ model_str = model_field.to_s.strip
169
+
170
+ # Handle 'inherit' keyword
171
+ return :inherit if model_str == "inherit"
172
+
173
+ # Resolve using SwarmSDK model aliases
174
+ # This maps 'sonnet' → 'claude-sonnet-4-5-20250929', etc.
175
+ SwarmSDK::Models.resolve_alias(model_str)
176
+ end
177
+
178
+ # Warn about unknown frontmatter fields
179
+ def warn_unknown_fields(frontmatter)
180
+ unknown_fields = frontmatter.keys - SUPPORTED_FIELDS
181
+
182
+ unknown_fields.each do |field|
183
+ @warnings << case field
184
+ when "hooks"
185
+ "Hooks configuration detected in agent frontmatter. SwarmSDK handles hooks at the swarm level. See: #{SWARM_SDK_DOCS_URL}"
186
+ else
187
+ "Unknown field '#{field}' in Claude Code agent file. Ignoring. Supported fields: #{SUPPORTED_FIELDS.join(", ")}"
188
+ end
189
+ end
190
+ end
191
+
192
+ # Emit all collected warnings via LogCollector
193
+ def emit_warnings(agent_name)
194
+ return if @warnings.empty?
195
+
196
+ @warnings.each do |warning|
197
+ LogCollector.emit(
198
+ type: "claude_code_conversion_warning",
199
+ agent: agent_name,
200
+ message: warning,
201
+ )
202
+ end
203
+ end
204
+ end
205
+ end
@@ -116,6 +116,11 @@ module SwarmSDK
116
116
  return unless @config[:swarm]
117
117
 
118
118
  @all_agents_config = @config[:swarm][:all_agents] || {}
119
+
120
+ # Convert disable_default_tools array elements to symbols
121
+ if @all_agents_config[:disable_default_tools].is_a?(Array)
122
+ @all_agents_config[:disable_default_tools] = @all_agents_config[:disable_default_tools].map(&:to_sym)
123
+ end
119
124
  end
120
125
 
121
126
  def load_hooks_config
@@ -212,6 +217,9 @@ module SwarmSDK
212
217
  # Merge headers: all_agents.headers + agent.headers
213
218
  # Agent values override all_agents values for same keys
214
219
  merged[:headers] = (merged[:headers] || {}).merge(value || {})
220
+ when :disable_default_tools
221
+ # Convert array elements to symbols if it's an array
222
+ merged[key] = value.is_a?(Array) ? value.map(&:to_sym) : value
215
223
  else
216
224
  # For everything else (model, provider, base_url, timeout, coding_agent, etc.),
217
225
  # agent value overrides all_agents value
@@ -238,8 +246,10 @@ module SwarmSDK
238
246
  # Parse markdown and merge with YAML config
239
247
  agent_def_from_file = MarkdownParser.parse(content, name)
240
248
 
241
- # Merge: markdown file overrides merged_config for fields it defines
242
- final_config = merged_config.merge(agent_def_from_file.to_h.compact)
249
+ # Merge: YAML config overrides markdown file (YAML takes precedence)
250
+ # This allows YAML to override any settings from the markdown file
251
+ final_config = agent_def_from_file.to_h.compact.merge(merged_config.compact)
252
+
243
253
  Agent::Definition.new(name, final_config)
244
254
  rescue StandardError => e
245
255
  raise ConfigurationError, "Error loading agent '#{name}' from file '#{file_path}': #{e.message}"
@@ -1,12 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SwarmSDK
4
+ # Parser for agent markdown files with YAML frontmatter
5
+ #
6
+ # Supports two formats:
7
+ # 1. SwarmSDK format - YAML frontmatter with array-based tools
8
+ # 2. Claude Code format - Detected and converted via ClaudeCodeAgentAdapter
9
+ #
10
+ # Format detection is automatic based on frontmatter structure.
4
11
  class MarkdownParser
5
12
  FRONTMATTER_PATTERN = /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
6
13
 
7
14
  class << self
15
+ # Parse markdown content into an Agent::Definition
16
+ #
17
+ # Automatically detects format (SwarmSDK or Claude Code) and routes
18
+ # to appropriate parser.
19
+ #
20
+ # @param content [String] Markdown content with YAML frontmatter
21
+ # @param agent_name [Symbol, String, nil] Name of the agent
22
+ # @return [Agent::Definition] Parsed agent definition
23
+ # @raise [ConfigurationError] if format is invalid
8
24
  def parse(content, agent_name = nil)
9
- new(content, agent_name).parse
25
+ # Detect Claude Code format and route to adapter
26
+ if ClaudeCodeAgentAdapter.claude_code_format?(content)
27
+ config = ClaudeCodeAgentAdapter.parse(content, agent_name)
28
+ # For Claude Code format, agent_name parameter is required since
29
+ # the 'name' field in frontmatter is Claude Code specific and not used
30
+ unless agent_name
31
+ raise ConfigurationError, "Agent name must be provided when parsing Claude Code format"
32
+ end
33
+
34
+ Agent::Definition.new(agent_name.to_sym, config)
35
+ else
36
+ # Use standard SwarmSDK format parsing
37
+ new(content, agent_name).parse
38
+ end
10
39
  end
11
40
  end
12
41
 
@@ -0,0 +1,5 @@
1
+ {
2
+ "sonnet": "claude-sonnet-4-5-20250929",
3
+ "opus": "claude-opus-4-1-20250805",
4
+ "haiku": "claude-haiku-4-5-20251001"
5
+ }
@@ -0,0 +1 @@
1
+ [{"name":"Claude Sonnet 4.5","id":"claude-sonnet-4-5-20250929","provider":"anthropic","family":"claude-sonnet-4-5","context_window":200000,"max_output_tokens":64000,"modalities":{"input":["image","text"],"output":["text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":3.0,"cached_input_per_million":3.75,"output_per_million":15.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Claude Haiku 4.5","id":"claude-haiku-4-5-20251001","provider":"anthropic","family":"claude-haiku-4-5","context_window":200000,"max_output_tokens":64000,"modalities":{"input":["image","text"],"output":["text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.0,"cached_input_per_million":1.25,"output_per_million":5.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Claude Opus 4.1","id":"claude-opus-4-1-20250805","provider":"anthropic","family":"claude-opus-4-1","context_window":200000,"max_output_tokens":32000,"modalities":{"input":["image","text"],"output":["text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":18.75,"output_per_million":75.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.5 Pro","id":"gemini-2.5-pro","provider":"gemini","family":"gemini-2.5-pro","context_window":1048576,"max_output_tokens":65536,"modalities":{"input":["audio","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.25,"cached_input_per_million":0.125,"output_per_million":10.0},"batch":{"input_per_million":0.625,"output_per_million":5.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.5 Flash","id":"gemini-2.5-flash","provider":"gemini","family":"gemini-2.5-flash","context_window":1048576,"max_output_tokens":65536,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.3,"cached_input_per_million":0.03,"output_per_million":2.5},"batch":{"input_per_million":0.15,"output_per_million":1.25}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.5 Flash-Lite","id":"gemini-2.5-flash-lite","provider":"gemini","family":"gemini-2.5-flash-lite","context_window":1048576,"max_output_tokens":65536,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.3,"cached_input_per_million":0.03,"output_per_million":2.5},"batch":{"input_per_million":0.15,"output_per_million":1.25}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.0 Flash","id":"gemini-2.0-flash","provider":"gemini","family":"gemini-2.0-flash","context_window":1048576,"max_output_tokens":8192,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.0 Flash","id":"gemini-2.0-flash-001","provider":"gemini","family":"gemini-2.0-flash","context_window":1048576,"max_output_tokens":8192,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.0 Flash","id":"gemini-2.0-flash-exp","provider":"gemini","family":"gemini-2.0-flash","context_window":1048576,"max_output_tokens":8192,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.0 Flash-Lite","id":"gemini-2.0-flash-lite","provider":"gemini","family":"gemini-2.0-flash-lite","context_window":1048576,"max_output_tokens":8192,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Gemini 2.0 Flash-Lite","id":"gemini-2.0-flash-lite-001","provider":"gemini","family":"gemini-2.0-flash-lite","context_window":1048576,"max_output_tokens":8192,"modalities":{"input":["audio","image","text"],"output":["text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"deepseek-chat","id":"deepseek-chat","provider":"deepseek","family":"deepseek-chat","context_window":128000,"max_output_tokens":8000,"modalities":{"input":["text"],"output":["text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.28,"cached_input_per_million":0.028,"output_per_million":0.42},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"deepseek-reasoner","id":"deepseek-reasoner","provider":"deepseek","family":"deepseek-reasoner","context_window":null,"max_output_tokens":64000,"modalities":{"input":["text"],"output":["text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5","family":"gpt-5","provider":"openai","id":"gpt-5-2025-08-07","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.25,"cached_input_per_million":0.125,"output_per_million":10.0},"batch":{"input_per_million":0.625,"output_per_million":5.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5","family":"gpt-5","provider":"openai","id":"gpt-5","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.25,"cached_input_per_million":0.125,"output_per_million":10.0},"batch":{"input_per_million":0.625,"output_per_million":5.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 mini","family":"gpt-5-mini","provider":"openai","id":"gpt-5-mini-2025-08-07","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.25,"cached_input_per_million":0.025,"output_per_million":2.0},"batch":{"input_per_million":0.125,"output_per_million":1.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 mini","family":"gpt-5-mini","provider":"openai","id":"gpt-5-mini","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.25,"cached_input_per_million":0.025,"output_per_million":2.0},"batch":{"input_per_million":0.125,"output_per_million":1.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 nano","family":"gpt-5-nano","provider":"openai","id":"gpt-5-nano-2025-08-07","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.05,"cached_input_per_million":0.005,"output_per_million":0.4},"batch":{"input_per_million":0.025,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 nano","family":"gpt-5-nano","provider":"openai","id":"gpt-5-nano","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.05,"cached_input_per_million":0.005,"output_per_million":0.4},"batch":{"input_per_million":0.025,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 pro","family":"gpt-5-pro","provider":"openai","id":"gpt-5-pro-2025-10-06","context_window":400000,"max_output_tokens":272000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":null,"output_per_million":120.0},"batch":{"input_per_million":7.5,"output_per_million":60.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 pro","family":"gpt-5-pro","provider":"openai","id":"gpt-5-pro","context_window":400000,"max_output_tokens":272000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":null,"output_per_million":120.0},"batch":{"input_per_million":7.5,"output_per_million":60.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.1","family":"gpt-4.1","provider":"openai","id":"gpt-4.1-2025-04-14","context_window":1047576,"max_output_tokens":32768,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.5,"output_per_million":8.0},"batch":{"input_per_million":1.0,"output_per_million":4.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.1","family":"gpt-4.1","provider":"openai","id":"gpt-4.1","context_window":1047576,"max_output_tokens":32768,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.5,"output_per_million":8.0},"batch":{"input_per_million":1.0,"output_per_million":4.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-oss-120b","family":"gpt-oss-120b","provider":"openai","id":"gpt-oss-120b","context_window":131072,"max_output_tokens":131072,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-oss-20b","family":"gpt-oss-20b","provider":"openai","id":"gpt-oss-20b","context_window":131072,"max_output_tokens":131072,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Sora 2","family":"sora-2","provider":"openai","id":"sora-2","context_window":null,"max_output_tokens":null,"modalities":{"input":["image","text"],"output":["audio","embeddings"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Sora 2 Pro","family":"sora-2-pro","provider":"openai","id":"sora-2-pro","context_window":null,"max_output_tokens":null,"modalities":{"input":["image","text"],"output":["audio","embeddings"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3-deep-research","family":"o3-deep-research","provider":"openai","id":"o3-deep-research-2025-06-26","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":10.0,"cached_input_per_million":2.5,"output_per_million":40.0},"batch":{"input_per_million":5.0,"output_per_million":20.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3-deep-research","family":"o3-deep-research","provider":"openai","id":"o3-deep-research","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":10.0,"cached_input_per_million":2.5,"output_per_million":40.0},"batch":{"input_per_million":5.0,"output_per_million":20.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o4-mini-deep-research","family":"o4-mini-deep-research","provider":"openai","id":"o4-mini-deep-research-2025-06-26","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.5,"output_per_million":8.0},"batch":{"input_per_million":1.0,"output_per_million":4.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o4-mini-deep-research","family":"o4-mini-deep-research","provider":"openai","id":"o4-mini-deep-research","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.5,"output_per_million":8.0},"batch":{"input_per_million":1.0,"output_per_million":4.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT Image 1","family":"gpt-image-1","provider":"openai","id":"gpt-image-1","context_window":null,"max_output_tokens":null,"modalities":{"input":["image","text"],"output":["embeddings","image"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":5.0,"cached_input_per_million":1.25,"output_per_million":40.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-image-1-mini","family":"gpt-image-1-mini","provider":"openai","id":"gpt-image-1-mini","context_window":null,"max_output_tokens":null,"modalities":{"input":["image","text"],"output":["embeddings","image"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.2,"output_per_million":8.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"DALL·E 3","family":"dall-e-3","provider":"openai","id":"dall-e-3","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["embeddings","image"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini TTS","family":"gpt-4o-mini-tts","provider":"openai","id":"gpt-4o-mini-tts","context_window":2000,"max_output_tokens":null,"modalities":{"input":["text"],"output":["audio","embeddings"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":null,"output_per_million":12.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o Transcribe","family":"gpt-4o-transcribe","provider":"openai","id":"gpt-4o-transcribe","context_window":16000,"max_output_tokens":2000,"modalities":{"input":["audio","text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Transcribe","family":"gpt-4o-mini-transcribe","provider":"openai","id":"gpt-4o-mini-transcribe","context_window":16000,"max_output_tokens":2000,"modalities":{"input":["audio","text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":1.25,"cached_input_per_million":null,"output_per_million":5.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-realtime","family":"gpt-realtime","provider":"openai","id":"gpt-realtime-2025-08-28","context_window":32000,"max_output_tokens":4096,"modalities":{"input":["audio","image","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":4.0,"cached_input_per_million":0.5,"output_per_million":16.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-realtime","family":"gpt-realtime","provider":"openai","id":"gpt-realtime","context_window":32000,"max_output_tokens":4096,"modalities":{"input":["audio","image","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":4.0,"cached_input_per_million":0.5,"output_per_million":16.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-audio","family":"gpt-audio","provider":"openai","id":"gpt-audio-2025-08-28","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-audio","family":"gpt-audio","provider":"openai","id":"gpt-audio","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-realtime-mini","family":"gpt-realtime-mini","provider":"openai","id":"gpt-realtime-mini-2025-10-06","context_window":32000,"max_output_tokens":4096,"modalities":{"input":["audio","image","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":0.06,"output_per_million":2.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-realtime-mini","family":"gpt-realtime-mini","provider":"openai","id":"gpt-realtime-mini","context_window":32000,"max_output_tokens":4096,"modalities":{"input":["audio","image","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":0.06,"output_per_million":2.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-audio-mini","family":"gpt-audio-mini","provider":"openai","id":"gpt-audio-mini-2025-10-06","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":null,"output_per_million":2.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-audio-mini","family":"gpt-audio-mini","provider":"openai","id":"gpt-audio-mini","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":null,"output_per_million":2.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5 Chat","family":"gpt-5-chat-latest","provider":"openai","id":"gpt-5-chat-latest","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.25,"cached_input_per_million":0.125,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"ChatGPT-4o","family":"chatgpt-4o-latest","provider":"openai","id":"chatgpt-4o-latest","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":5.0,"cached_input_per_million":null,"output_per_million":15.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-5-Codex","family":"gpt-5-codex","provider":"openai","id":"gpt-5-codex","context_window":400000,"max_output_tokens":128000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.25,"cached_input_per_million":0.125,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3-pro","family":"o3-pro","provider":"openai","id":"o3-pro-2025-06-10","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":20.0,"cached_input_per_million":null,"output_per_million":80.0},"batch":{"input_per_million":10.0,"output_per_million":40.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3-pro","family":"o3-pro","provider":"openai","id":"o3-pro","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":20.0,"cached_input_per_million":null,"output_per_million":80.0},"batch":{"input_per_million":10.0,"output_per_million":40.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3","family":"o3","provider":"openai","id":"o3-2025-04-16","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.5,"output_per_million":8.0},"batch":{"input_per_million":1.0,"output_per_million":4.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3","family":"o3","provider":"openai","id":"o3","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":0.5,"output_per_million":8.0},"batch":{"input_per_million":1.0,"output_per_million":4.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o4-mini","family":"o4-mini","provider":"openai","id":"o4-mini-2025-04-16","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.1,"cached_input_per_million":0.275,"output_per_million":4.4},"batch":{"input_per_million":0.55,"output_per_million":2.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o4-mini","family":"o4-mini","provider":"openai","id":"o4-mini","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.1,"cached_input_per_million":0.275,"output_per_million":4.4},"batch":{"input_per_million":0.55,"output_per_million":2.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.1 mini","family":"gpt-4.1-mini","provider":"openai","id":"gpt-4.1-mini-2025-04-14","context_window":1047576,"max_output_tokens":32768,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.4,"cached_input_per_million":0.1,"output_per_million":1.6},"batch":{"input_per_million":0.2,"output_per_million":0.8}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.1 mini","family":"gpt-4.1-mini","provider":"openai","id":"gpt-4.1-mini","context_window":1047576,"max_output_tokens":32768,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.4,"cached_input_per_million":0.1,"output_per_million":1.6},"batch":{"input_per_million":0.2,"output_per_million":0.8}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.1 nano","family":"gpt-4.1-nano","provider":"openai","id":"gpt-4.1-nano-2025-04-14","context_window":1047576,"max_output_tokens":32768,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.1 nano","family":"gpt-4.1-nano","provider":"openai","id":"gpt-4.1-nano","context_window":1047576,"max_output_tokens":32768,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":0.025,"output_per_million":0.4},"batch":{"input_per_million":0.05,"output_per_million":0.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1-pro","family":"o1-pro","provider":"openai","id":"o1-pro-2025-03-19","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":150.0,"cached_input_per_million":null,"output_per_million":600.0},"batch":{"input_per_million":75.0,"output_per_million":300.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1-pro","family":"o1-pro","provider":"openai","id":"o1-pro","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":150.0,"cached_input_per_million":null,"output_per_million":600.0},"batch":{"input_per_million":75.0,"output_per_million":300.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"computer-use-preview","family":"computer-use-preview","provider":"openai","id":"computer-use-preview-2025-03-11","context_window":8192,"max_output_tokens":1024,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":3.0,"cached_input_per_million":null,"output_per_million":12.0},"batch":{"input_per_million":1.5,"output_per_million":6.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"computer-use-preview","family":"computer-use-preview","provider":"openai","id":"computer-use-preview","context_window":8192,"max_output_tokens":1024,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":3.0,"cached_input_per_million":null,"output_per_million":12.0},"batch":{"input_per_million":1.5,"output_per_million":6.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Search Preview","family":"gpt-4o-mini-search-preview","provider":"openai","id":"gpt-4o-mini-search-preview-2025-03-11","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.15,"cached_input_per_million":null,"output_per_million":0.6},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Search Preview","family":"gpt-4o-mini-search-preview","provider":"openai","id":"gpt-4o-mini-search-preview","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.15,"cached_input_per_million":null,"output_per_million":0.6},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o Search Preview","family":"gpt-4o-search-preview","provider":"openai","id":"gpt-4o-search-preview-2025-03-11","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o Search Preview","family":"gpt-4o-search-preview","provider":"openai","id":"gpt-4o-search-preview","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.5 Preview (Deprecated)","family":"gpt-4.5-preview","provider":"openai","id":"gpt-4.5-preview-2025-02-27","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":75.0,"cached_input_per_million":37.5,"output_per_million":150.0},"batch":{"input_per_million":37.5,"output_per_million":75.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4.5 Preview (Deprecated)","family":"gpt-4.5-preview","provider":"openai","id":"gpt-4.5-preview","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":75.0,"cached_input_per_million":37.5,"output_per_million":150.0},"batch":{"input_per_million":37.5,"output_per_million":75.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3-mini","family":"o3-mini","provider":"openai","id":"o3-mini-2025-01-31","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.1,"cached_input_per_million":0.55,"output_per_million":4.4},"batch":{"input_per_million":0.55,"output_per_million":2.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o3-mini","family":"o3-mini","provider":"openai","id":"o3-mini","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.1,"cached_input_per_million":0.55,"output_per_million":4.4},"batch":{"input_per_million":0.55,"output_per_million":2.2}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Audio","family":"gpt-4o-mini-audio-preview","provider":"openai","id":"gpt-4o-mini-audio-preview-2024-12-17","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.15,"cached_input_per_million":null,"output_per_million":0.6},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Audio","family":"gpt-4o-mini-audio-preview","provider":"openai","id":"gpt-4o-mini-audio-preview","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.15,"cached_input_per_million":null,"output_per_million":0.6},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Realtime","family":"gpt-4o-mini-realtime-preview","provider":"openai","id":"gpt-4o-mini-realtime-preview-2024-12-17","context_window":16000,"max_output_tokens":4096,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":0.3,"output_per_million":2.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini Realtime","family":"gpt-4o-mini-realtime-preview","provider":"openai","id":"gpt-4o-mini-realtime-preview","context_window":16000,"max_output_tokens":4096,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.6,"cached_input_per_million":0.3,"output_per_million":2.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1","family":"o1","provider":"openai","id":"o1-2024-12-17","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":7.5,"output_per_million":60.0},"batch":{"input_per_million":7.5,"output_per_million":30.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1","family":"o1","provider":"openai","id":"o1","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":7.5,"output_per_million":60.0},"batch":{"input_per_million":7.5,"output_per_million":30.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"omni-moderation","family":"omni-moderation-latest","provider":"openai","id":"omni-moderation-latest","context_window":null,"max_output_tokens":null,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":0.0,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1-mini","family":"o1-mini","provider":"openai","id":"o1-mini-2024-09-12","context_window":128000,"max_output_tokens":65536,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":1.1,"cached_input_per_million":0.55,"output_per_million":4.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1-mini","family":"o1-mini","provider":"openai","id":"o1-mini","context_window":128000,"max_output_tokens":65536,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":1.1,"cached_input_per_million":0.55,"output_per_million":4.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1 Preview","family":"o1-preview","provider":"openai","id":"o1-preview-2024-09-12","context_window":128000,"max_output_tokens":32768,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":7.5,"output_per_million":60.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"o1 Preview","family":"o1-preview","provider":"openai","id":"o1-preview","context_window":128000,"max_output_tokens":32768,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":7.5,"output_per_million":60.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o","family":"gpt-4o","provider":"openai","id":"gpt-4o-2024-08-06","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":1.25,"output_per_million":10.0},"batch":{"input_per_million":1.25,"output_per_million":5.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o","family":"gpt-4o","provider":"openai","id":"gpt-4o","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":1.25,"output_per_million":10.0},"batch":{"input_per_million":1.25,"output_per_million":5.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o Audio","family":"gpt-4o-audio-preview","provider":"openai","id":"gpt-4o-audio-preview","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini","family":"gpt-4o-mini","provider":"openai","id":"gpt-4o-mini-2024-07-18","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.15,"cached_input_per_million":0.075,"output_per_million":0.6},"batch":{"input_per_million":0.075,"output_per_million":0.3}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o mini","family":"gpt-4o-mini","provider":"openai","id":"gpt-4o-mini","context_window":128000,"max_output_tokens":16384,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.15,"cached_input_per_million":0.075,"output_per_million":0.6},"batch":{"input_per_million":0.075,"output_per_million":0.3}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4o Realtime","family":"gpt-4o-realtime-preview","provider":"openai","id":"gpt-4o-realtime-preview","context_window":32000,"max_output_tokens":4096,"modalities":{"input":["audio","text"],"output":["audio","embeddings","text"]},"capabilities":["function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":5.0,"cached_input_per_million":2.5,"output_per_million":20.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4 Turbo","family":"gpt-4-turbo","provider":"openai","id":"gpt-4-turbo-2024-04-09","context_window":128000,"max_output_tokens":4096,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":10.0,"cached_input_per_million":null,"output_per_million":30.0},"batch":{"input_per_million":5.0,"output_per_million":15.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4 Turbo","family":"gpt-4-turbo","provider":"openai","id":"gpt-4-turbo","context_window":128000,"max_output_tokens":4096,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["batch","function_calling"],"pricing":{"text_tokens":{"standard":{"input_per_million":10.0,"cached_input_per_million":null,"output_per_million":30.0},"batch":{"input_per_million":5.0,"output_per_million":15.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"babbage-002","family":"babbage-002","provider":"openai","id":"babbage-002","context_window":null,"max_output_tokens":16384,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":0.4,"cached_input_per_million":null,"output_per_million":0.4},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"codex-mini-latest","family":"codex-mini-latest","provider":"openai","id":"codex-mini-latest","context_window":200000,"max_output_tokens":100000,"modalities":{"input":["image","text"],"output":["embeddings","text"]},"capabilities":["function_calling","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":1.5,"cached_input_per_million":0.375,"output_per_million":6.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"DALL·E 2","family":"dall-e-2","provider":"openai","id":"dall-e-2","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["embeddings","image"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"davinci-002","family":"davinci-002","provider":"openai","id":"davinci-002","context_window":null,"max_output_tokens":16384,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":2.0,"cached_input_per_million":null,"output_per_million":2.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-3.5 Turbo","family":"gpt-3.5-turbo","provider":"openai","id":"gpt-3.5-turbo","context_window":16385,"max_output_tokens":4096,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.5,"cached_input_per_million":null,"output_per_million":1.5},"batch":{"input_per_million":0.25,"output_per_million":0.75}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4","family":"gpt-4","provider":"openai","id":"gpt-4-0613","context_window":8192,"max_output_tokens":8192,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":30.0,"cached_input_per_million":null,"output_per_million":60.0},"batch":{"input_per_million":15.0,"output_per_million":30.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4","family":"gpt-4","provider":"openai","id":"gpt-4","context_window":8192,"max_output_tokens":8192,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":30.0,"cached_input_per_million":null,"output_per_million":60.0},"batch":{"input_per_million":15.0,"output_per_million":30.0}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"GPT-4 Turbo Preview","family":"gpt-4-turbo-preview","provider":"openai","id":"gpt-4-turbo-preview","context_window":128000,"max_output_tokens":4096,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":10.0,"cached_input_per_million":null,"output_per_million":30.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"gpt-4o-transcribe-diarize","family":"gpt-4o-transcribe-diarize","provider":"openai","id":"gpt-4o-transcribe-diarize","context_window":16000,"max_output_tokens":2000,"modalities":{"input":["audio","text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":2.5,"cached_input_per_million":null,"output_per_million":10.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"text-embedding-3-large","family":"text-embedding-3-large","provider":"openai","id":"text-embedding-3-large","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.13,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":0.065,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":0.13},"batch":{"input_per_million":0.065}}}},{"name":"text-embedding-3-small","family":"text-embedding-3-small","provider":"openai","id":"text-embedding-3-small","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch","structured_output"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.02,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":0.01,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":0.02},"batch":{"input_per_million":0.01}}}},{"name":"text-embedding-ada-002","family":"text-embedding-ada-002","provider":"openai","id":"text-embedding-ada-002","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":["batch"],"pricing":{"text_tokens":{"standard":{"input_per_million":0.1,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":0.05,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":0.1},"batch":{"input_per_million":0.05}}}},{"name":"text-moderation","family":"text-moderation-latest","provider":"openai","id":"text-moderation-latest","context_window":null,"max_output_tokens":32768,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":0.0,"cached_input_per_million":null,"output_per_million":0.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"text-moderation-stable","family":"text-moderation-stable","provider":"openai","id":"text-moderation-stable","context_window":null,"max_output_tokens":32768,"modalities":{"input":["text"],"output":["embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":0.0,"cached_input_per_million":null,"output_per_million":0.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"TTS-1","family":"tts-1","provider":"openai","id":"tts-1","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["audio","embeddings"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":15.0,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"TTS-1 HD","family":"tts-1-hd","provider":"openai","id":"tts-1-hd","context_window":null,"max_output_tokens":null,"modalities":{"input":["text"],"output":["audio","embeddings"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":null,"cached_input_per_million":null,"output_per_million":30.0},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}},{"name":"Whisper","family":"whisper-1","provider":"openai","id":"whisper-1","context_window":null,"max_output_tokens":null,"modalities":{"input":["audio"],"output":["audio","embeddings","text"]},"capabilities":[],"pricing":{"text_tokens":{"standard":{"input_per_million":0.006,"cached_input_per_million":null,"output_per_million":null},"batch":{"input_per_million":null,"output_per_million":null}},"embeddings":{"standard":{"input_per_million":null},"batch":{"input_per_million":null}}}}]
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ # Models provides model validation and suggestion functionality
5
+ #
6
+ # Uses static JSON files:
7
+ # - models.json: Curated model list from Parsera
8
+ # - model_aliases.json: Shortcuts mapping to latest models
9
+ #
10
+ # This avoids network calls, API key requirements, and RubyLLM
11
+ # registry manipulation.
12
+ #
13
+ # @example
14
+ # model = SwarmSDK::Models.find("claude-sonnet-4-5-20250929")
15
+ # model = SwarmSDK::Models.find("sonnet") # Uses alias
16
+ # suggestions = SwarmSDK::Models.suggest_similar("anthropic:claude-sonnet-4-5")
17
+ class Models
18
+ MODELS_JSON_PATH = File.expand_path("models.json", __dir__)
19
+ ALIASES_JSON_PATH = File.expand_path("model_aliases.json", __dir__)
20
+
21
+ class << self
22
+ # Find a model by ID or alias
23
+ #
24
+ # @param model_id [String] Model ID or alias to find
25
+ # @return [Hash, nil] Model data or nil if not found
26
+ def find(model_id)
27
+ # Check if it's an alias first
28
+ resolved_id = resolve_alias(model_id)
29
+
30
+ all.find { |m| m["id"] == resolved_id || m[:id] == resolved_id }
31
+ end
32
+
33
+ # Resolve a model alias to full model ID
34
+ #
35
+ # @param model_id [String] Model ID or alias
36
+ # @return [String] Resolved model ID (or original if not an alias)
37
+ def resolve_alias(model_id)
38
+ aliases[model_id.to_s] || model_id
39
+ end
40
+
41
+ # Suggest similar models for a given query
42
+ #
43
+ # Strips provider prefixes and normalizes for fuzzy matching.
44
+ #
45
+ # @param query [String] Model ID to match against
46
+ # @param limit [Integer] Maximum number of suggestions
47
+ # @return [Array<Hash>] Up to `limit` similar models
48
+ def suggest_similar(query, limit: 3)
49
+ # Strip provider prefix (e.g., "anthropic:claude-sonnet-4-5" → "claude-sonnet-4-5")
50
+ query_without_prefix = query.to_s.sub(/^[^:]+:/, "")
51
+ normalized_query = query_without_prefix.downcase.gsub(/[.\-_]/, "")
52
+
53
+ matches = all.select do |model|
54
+ model_id = (model["id"] || model[:id]).to_s
55
+ model_name = (model["name"] || model[:name]).to_s
56
+
57
+ normalized_id = model_id.downcase.gsub(/[.\-_]/, "")
58
+ normalized_name = model_name.downcase.gsub(/[.\-_]/, "")
59
+
60
+ normalized_id.include?(normalized_query) || normalized_name.include?(normalized_query)
61
+ end.first(limit)
62
+
63
+ matches.map do |m|
64
+ {
65
+ id: m["id"] || m[:id],
66
+ name: m["name"] || m[:name],
67
+ context_window: m["context_window"] || m[:context_window],
68
+ }
69
+ end
70
+ end
71
+
72
+ # Get all models
73
+ #
74
+ # @return [Array<Hash>] All models from models.json
75
+ def all
76
+ @models ||= load_models
77
+ end
78
+
79
+ # Get all aliases
80
+ #
81
+ # @return [Hash] Alias mappings
82
+ def aliases
83
+ @aliases ||= load_aliases
84
+ end
85
+
86
+ # Reload models and aliases from JSON files
87
+ #
88
+ # @return [Array<Hash>] Loaded models
89
+ def reload!
90
+ @models = load_models
91
+ @aliases = load_aliases
92
+ @models
93
+ end
94
+
95
+ private
96
+
97
+ # Load models from JSON file
98
+ #
99
+ # @return [Array<Hash>] Models array
100
+ def load_models
101
+ JSON.parse(File.read(MODELS_JSON_PATH))
102
+ rescue StandardError => e
103
+ # Log error and return empty array
104
+ RubyLLM.logger.error("Failed to load SwarmSDK models.json: #{e.class} - #{e.message}")
105
+ []
106
+ end
107
+
108
+ # Load aliases from JSON file
109
+ #
110
+ # @return [Hash] Alias mappings
111
+ def load_aliases
112
+ JSON.parse(File.read(ALIASES_JSON_PATH))
113
+ rescue StandardError => e
114
+ # Log error and return empty hash
115
+ RubyLLM.logger.debug("Failed to load SwarmSDK model_aliases.json: #{e.class} - #{e.message}")
116
+ {}
117
+ end
118
+ end
119
+ end
120
+ end
@@ -94,16 +94,19 @@ module SwarmSDK
94
94
  # You build APIs.
95
95
  # MD
96
96
  def agent(name, content = nil, &block)
97
- # Case 1: agent :name, <<~MD (name + markdown content)
98
- if content.is_a?(String) && !block_given? && markdown_content?(content)
97
+ # Case 1: agent :name, <<~MD do ... end (markdown + overrides)
98
+ if content.is_a?(String) && block_given? && markdown_content?(content)
99
+ load_agent_from_markdown_with_overrides(content, name, &block)
100
+ # Case 2: agent :name, <<~MD (markdown only)
101
+ elsif content.is_a?(String) && !block_given? && markdown_content?(content)
99
102
  load_agent_from_markdown(content, name)
100
- # Case 2: agent :name do ... end (inline DSL)
103
+ # Case 3: agent :name do ... end (inline DSL)
101
104
  elsif block_given?
102
105
  builder = Agent::Builder.new(name)
103
106
  builder.instance_eval(&block)
104
107
  @agents[name] = builder
105
108
  else
106
- raise ArgumentError, "Invalid agent definition. Use: agent :name { ... } OR agent :name, <<~MD ... MD"
109
+ raise ArgumentError, "Invalid agent definition. Use: agent :name { ... } OR agent :name, <<~MD ... MD OR agent :name, <<~MD do ... end"
107
110
  end
108
111
  end
109
112
 
@@ -225,6 +228,76 @@ module SwarmSDK
225
228
  @agents[definition.name] = { __file_config__: definition.to_h }
226
229
  end
227
230
 
231
+ # Load an agent from markdown content with DSL overrides
232
+ #
233
+ # This allows loading from a file and then overriding specific settings:
234
+ # agent :reviewer, File.read("reviewer.md") do
235
+ # provider :openai
236
+ # model "gpt-4o"
237
+ # end
238
+ #
239
+ # @param content [String] Markdown content with frontmatter
240
+ # @param name_override [Symbol, nil] Optional name to override frontmatter name
241
+ # @yield Block with DSL overrides
242
+ # @return [void]
243
+ def load_agent_from_markdown_with_overrides(content, name_override = nil, &block)
244
+ # Parse markdown content first
245
+ definition = MarkdownParser.parse(content, name_override)
246
+
247
+ # Create a builder with the markdown config
248
+ builder = Agent::Builder.new(definition.name)
249
+
250
+ # Apply markdown settings to builder (these become the base)
251
+ apply_definition_to_builder(builder, definition.to_h)
252
+
253
+ # Apply DSL overrides (these override the markdown settings)
254
+ builder.instance_eval(&block)
255
+
256
+ # Store the builder (not file config) so overrides are preserved
257
+ @agents[definition.name] = builder
258
+ end
259
+
260
+ # Apply agent definition hash to a builder
261
+ #
262
+ # @param builder [Agent::Builder] Builder to configure
263
+ # @param config [Hash] Configuration hash from definition
264
+ # @return [void]
265
+ def apply_definition_to_builder(builder, config)
266
+ builder.description(config[:description]) if config[:description]
267
+ builder.model(config[:model]) if config[:model]
268
+ builder.provider(config[:provider]) if config[:provider]
269
+ builder.base_url(config[:base_url]) if config[:base_url]
270
+ builder.api_version(config[:api_version]) if config[:api_version]
271
+ builder.context_window(config[:context_window]) if config[:context_window]
272
+ builder.system_prompt(config[:system_prompt]) if config[:system_prompt]
273
+ builder.directory(config[:directory]) if config[:directory]
274
+ builder.timeout(config[:timeout]) if config[:timeout]
275
+ builder.parameters(config[:parameters]) if config[:parameters]
276
+ builder.headers(config[:headers]) if config[:headers]
277
+ builder.coding_agent(config[:coding_agent]) unless config[:coding_agent].nil?
278
+ # Don't apply assume_model_exists from markdown - let DSL overrides or auto-enable handle it
279
+ # builder.assume_model_exists(config[:assume_model_exists]) unless config[:assume_model_exists].nil?
280
+ builder.bypass_permissions(config[:bypass_permissions]) if config[:bypass_permissions]
281
+ builder.disable_default_tools(config[:disable_default_tools]) unless config[:disable_default_tools].nil?
282
+
283
+ # Add tools from markdown
284
+ if config[:tools]&.any?
285
+ # Extract tool names from the tools array (which may be hashes with permissions)
286
+ tool_names = config[:tools].map do |tool|
287
+ tool.is_a?(Hash) ? tool[:name] : tool
288
+ end
289
+ builder.tools(*tool_names)
290
+ end
291
+
292
+ # Add delegates_to
293
+ builder.delegates_to(*config[:delegates_to]) if config[:delegates_to]&.any?
294
+
295
+ # Add MCP servers
296
+ config[:mcp_servers]&.each do |server|
297
+ builder.mcp_server(server[:name], **server.except(:name))
298
+ end
299
+ end
300
+
228
301
  # Build a traditional single-swarm execution
229
302
  #
230
303
  # @return [Swarm] Configured swarm instance
@@ -12,7 +12,7 @@ module SwarmSDK
12
12
  #
13
13
  # This encapsulates all tool-related logic that was previously in Swarm.
14
14
  class ToolConfigurator
15
- # Default tools available to all agents (unless include_default_tools: false)
15
+ # Default tools available to all agents (unless disable_default_tools is set)
16
16
  DEFAULT_TOOLS = [
17
17
  :Read,
18
18
  :Grep,
@@ -21,6 +21,7 @@ module SwarmSDK
21
21
  :ScratchpadWrite,
22
22
  :ScratchpadRead,
23
23
  :ScratchpadList,
24
+ :Think,
24
25
  ].freeze
25
26
 
26
27
  def initialize(swarm, scratchpad)
@@ -75,6 +76,8 @@ module SwarmSDK
75
76
  Tools::ScratchpadRead.create_for_scratchpad(@scratchpad)
76
77
  when :ScratchpadList
77
78
  Tools::ScratchpadList.create_for_scratchpad(@scratchpad)
79
+ when :Think
80
+ Tools::Think.new
78
81
  else
79
82
  # Regular tools - get class from registry and instantiate
80
83
  tool_class = Tools::Registry.get(tool_name_sym)
@@ -133,13 +136,14 @@ module SwarmSDK
133
136
  end
134
137
  end
135
138
 
136
- # Register default tools for agents that have include_default_tools enabled
139
+ # Register default tools for agents (unless disabled)
137
140
  #
138
141
  # @param chat [AgentChat] The chat instance
139
142
  # @param agent_name [Symbol] Agent name
140
143
  # @param agent_definition [AgentDefinition] Agent definition
141
144
  def register_default_tools(chat, agent_name:, agent_definition:)
142
- return unless agent_definition.include_default_tools
145
+ # If disable_default_tools is true, skip all default tools
146
+ return if agent_definition.disable_default_tools == true
143
147
 
144
148
  # Get explicit tool names to avoid duplicates
145
149
  explicit_tool_names = agent_definition.tools.map { |t| t[:name] }.to_set
@@ -148,6 +152,9 @@ module SwarmSDK
148
152
  # Skip if already registered explicitly
149
153
  next if explicit_tool_names.include?(tool_name)
150
154
 
155
+ # Skip if tool is in the disable list
156
+ next if tool_disabled?(tool_name, agent_definition.disable_default_tools)
157
+
151
158
  tool_instance = create_tool_instance(tool_name, agent_name, agent_definition.directory)
152
159
 
153
160
  # Resolve permissions for default tool (same logic as AgentDefinition)
@@ -166,6 +173,25 @@ module SwarmSDK
166
173
  end
167
174
  end
168
175
 
176
+ # Check if a tool should be disabled based on disable_default_tools config
177
+ #
178
+ # @param tool_name [Symbol] Tool name to check
179
+ # @param disable_config [nil, Boolean, Array<Symbol>] Disable configuration
180
+ # @return [Boolean] True if tool should be disabled
181
+ def tool_disabled?(tool_name, disable_config)
182
+ return false if disable_config.nil?
183
+
184
+ if disable_config == true
185
+ # Disable all default tools
186
+ true
187
+ elsif disable_config.is_a?(Array)
188
+ # Disable only tools in the array
189
+ disable_config.include?(tool_name)
190
+ else
191
+ false
192
+ end
193
+ end
194
+
169
195
  # Register agent delegation tools
170
196
  #
171
197
  # Creates delegation tools that allow one agent to call another.
@@ -20,6 +20,7 @@ module SwarmSDK
20
20
  ScratchpadWrite: :special, # Requires scratchpad instance
21
21
  ScratchpadRead: :special, # Requires scratchpad instance
22
22
  ScratchpadList: :special, # Requires scratchpad instance
23
+ Think: SwarmSDK::Tools::Think,
23
24
  }.freeze
24
25
 
25
26
  class << self
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module Tools
5
+ # Think tool for explicit reasoning and planning
6
+ #
7
+ # Allows the agent to write down thoughts, plans, strategies, and intermediate
8
+ # calculations. These thoughts become part of the conversation context, enabling
9
+ # better attention and reasoning through complex problems.
10
+ #
11
+ # This is inspired by research showing that explicitly articulating reasoning steps
12
+ # (chain-of-thought prompting) leads to significantly better outcomes, especially
13
+ # for complex tasks requiring multi-step reasoning or arithmetic.
14
+ class Think < RubyLLM::Tool
15
+ def name
16
+ "Think"
17
+ end
18
+
19
+ description <<~DESC
20
+ **IMPORTANT: You SHOULD use this tool frequently throughout your work. Using this tool leads to significantly
21
+ better outcomes and more accurate solutions. Make it a habit to think before acting.**
22
+
23
+ This tool allows you to write down your thoughts, plans, strategies, and intermediate calculations.
24
+ Think of it as your working memory - just as humans think before speaking or acting, you should think before
25
+ using other tools or providing responses.
26
+
27
+ **STRONGLY RECOMMENDED to use this tool:**
28
+ - **ALWAYS** before starting any task (even simple ones)
29
+ - **ALWAYS** when you need to do any arithmetic or counting
30
+ - **ALWAYS** after reading files or getting search results to process what you learned
31
+ - **FREQUENTLY** between steps to track progress and plan next actions
32
+
33
+ This is your private thinking space - use it liberally to enhance your problem-solving capabilities. Recording
34
+ your thoughts helps you maintain context across multiple steps and remember important information throughout your task.
35
+
36
+ When and how to use this tool:
37
+
38
+ 1. **Before starting any complex task**: Write down your understanding of the problem, break it into smaller
39
+ sub-tasks, and create a step-by-step plan. Example:
40
+ - "The user wants me to refactor this codebase. Let me first understand the structure..."
41
+ - "I need to: 1) Analyze current architecture, 2) Identify pain points, 3) Propose changes..."
42
+
43
+ 2. **For arithmetic and calculations**: Work through math problems step by step. Example:
44
+ - "If we have 150 requests/second and each takes 20ms, that's 150 * 0.02 = 3 seconds of CPU time..."
45
+ - "Converting 2GB to bytes: 2 * 1024 * 1024 * 1024 = 2,147,483,648 bytes"
46
+
47
+ 3. **After completing sub-tasks**: Summarize what you've accomplished and what remains. Example:
48
+ - "I've successfully implemented the authentication module. Next, I need to integrate it with the API..."
49
+ - "Fixed 3 out of 5 bugs. Remaining: memory leak in parser, race condition in worker thread"
50
+
51
+ 4. **When encountering complexity**: Break down complex logic or decisions. Example:
52
+ - "This function has multiple edge cases. Let me list them: null input, empty array, negative numbers..."
53
+ - "The user's request is ambiguous. Possible interpretations: A) modify existing code, B) create new module..."
54
+
55
+ 5. **For remembering context**: Store important information you'll need later. Example:
56
+ - "Important: The user mentioned they're using Ruby 3.2, so I can use pattern matching"
57
+ - "File structure: main.rb requires from lib/, config is in config.yml"
58
+
59
+ 6. **When debugging or analyzing**: Track your investigation process. Example:
60
+ - "The error occurs in line 42. Let me trace backwards: function called from main(), receives data from..."
61
+ - "Hypothesis: the bug might be due to timezone differences. Let me check..."
62
+
63
+ 7. **For creative problem-solving**: Brainstorm multiple approaches before choosing one. Example:
64
+ - "Approaches to optimize this: 1) Add caching, 2) Use parallel processing, 3) Optimize algorithm..."
65
+ - "Design patterns that could work here: Factory, Observer, or maybe Strategy pattern..."
66
+
67
+ **Remember: The most successful agents use this tool 5-10 times per task on average. If you haven't used this
68
+ tool in the last 2-3 actions, you probably should. Using this tool is a sign of thoughtful, methodical problem
69
+ solving and leads to fewer mistakes and better solutions.**
70
+
71
+ Your thoughts persist throughout your session as part of the conversation history, so you can refer
72
+ back to earlier thinking. Use clear formatting and organization to make it easy to reference
73
+ later. Don't hesitate to think out loud - this tool is designed to augment your cognitive capabilities and help
74
+ you deliver better solutions.
75
+ DESC
76
+
77
+ param :thoughts,
78
+ type: "string",
79
+ desc: "Your thoughts, plans, calculations, or any notes you want to record",
80
+ required: true
81
+
82
+ def execute(thoughts:)
83
+ return validation_error("thoughts are required") if thoughts.nil? || thoughts.empty?
84
+
85
+ "Thought noted."
86
+ end
87
+
88
+ private
89
+
90
+ def validation_error(message)
91
+ "<tool_use_error>InputValidationError: #{message}</tool_use_error>"
92
+ end
93
+ end
94
+ end
95
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SwarmSDK
4
- VERSION = "2.0.1"
4
+ VERSION = "2.0.3"
5
5
  end
data/lib/swarm_sdk.rb CHANGED
@@ -41,36 +41,6 @@ module SwarmSDK
41
41
  class StateError < Error; end
42
42
 
43
43
  class << self
44
- # Refresh RubyLLM model registry silently (without log output)
45
- #
46
- # By default, RubyLLM.models.refresh! outputs INFO level logs about
47
- # fetching models from providers. This method temporarily raises the
48
- # log level to suppress those messages, which is useful for CLI tools
49
- # that want clean output.
50
- #
51
- # If model refresh fails (e.g., missing API keys, invalid keys, network
52
- # unavailable), the error is silently caught and execution continues
53
- # using the bundled models.json. This allows SwarmSDK to work offline
54
- # and with dummy keys for local proxies.
55
- #
56
- # @example
57
- # SwarmSDK.refresh_models_silently
58
- #
59
- # @return [void]
60
- def refresh_models_silently
61
- original_level = RubyLLM.logger.level
62
- RubyLLM.logger.level = Logger::ERROR
63
-
64
- RubyLLM.models.refresh!
65
- rescue StandardError => e
66
- # Silently ignore all refresh failures
67
- # Models will use bundled models.json instead
68
- RubyLLM.logger.debug("Model refresh skipped: #{e.class} - #{e.message}")
69
- nil
70
- ensure
71
- RubyLLM.logger.level = original_level
72
- end
73
-
74
44
  # Main entry point for DSL
75
45
  def build(&block)
76
46
  Swarm::Builder.build(&block)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swarm_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Arruda
@@ -86,6 +86,7 @@ files:
86
86
  - lib/swarm_sdk/agent/chat/system_reminder_injector.rb
87
87
  - lib/swarm_sdk/agent/context.rb
88
88
  - lib/swarm_sdk/agent/definition.rb
89
+ - lib/swarm_sdk/claude_code_agent_adapter.rb
89
90
  - lib/swarm_sdk/configuration.rb
90
91
  - lib/swarm_sdk/context_compactor.rb
91
92
  - lib/swarm_sdk/context_compactor/metrics.rb
@@ -103,6 +104,9 @@ files:
103
104
  - lib/swarm_sdk/log_collector.rb
104
105
  - lib/swarm_sdk/log_stream.rb
105
106
  - lib/swarm_sdk/markdown_parser.rb
107
+ - lib/swarm_sdk/model_aliases.json
108
+ - lib/swarm_sdk/models.json
109
+ - lib/swarm_sdk/models.rb
106
110
  - lib/swarm_sdk/node/agent_config.rb
107
111
  - lib/swarm_sdk/node/builder.rb
108
112
  - lib/swarm_sdk/node/transformer_executor.rb
@@ -144,6 +148,7 @@ files:
144
148
  - lib/swarm_sdk/tools/stores/read_tracker.rb
145
149
  - lib/swarm_sdk/tools/stores/scratchpad.rb
146
150
  - lib/swarm_sdk/tools/stores/todo_manager.rb
151
+ - lib/swarm_sdk/tools/think.rb
147
152
  - lib/swarm_sdk/tools/todo_write.rb
148
153
  - lib/swarm_sdk/tools/write.rb
149
154
  - lib/swarm_sdk/utils.rb