claude_swarm 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +1 -1
  3. data/.claude/hooks/lint-code-files.rb +65 -0
  4. data/.rubocop.yml +22 -2
  5. data/CHANGELOG.md +21 -1
  6. data/CLAUDE.md +1 -1
  7. data/CONTRIBUTING.md +69 -0
  8. data/README.md +27 -2
  9. data/Rakefile +71 -3
  10. data/analyze_coverage.rb +94 -0
  11. data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
  12. data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
  13. data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
  14. data/docs/v2/README.md +308 -0
  15. data/docs/v2/guides/claude-code-agents.md +262 -0
  16. data/docs/v2/guides/complete-tutorial.md +3088 -0
  17. data/docs/v2/guides/getting-started.md +1456 -0
  18. data/docs/v2/guides/memory-adapters.md +998 -0
  19. data/docs/v2/guides/plugins.md +816 -0
  20. data/docs/v2/guides/quick-start-cli.md +1745 -0
  21. data/docs/v2/guides/rails-integration.md +1902 -0
  22. data/docs/v2/guides/swarm-memory.md +599 -0
  23. data/docs/v2/reference/cli.md +729 -0
  24. data/docs/v2/reference/ruby-dsl.md +2154 -0
  25. data/docs/v2/reference/yaml.md +1835 -0
  26. data/docs-team-swarm.yml +2222 -0
  27. data/examples/learning-assistant/assistant.md +7 -0
  28. data/examples/learning-assistant/example-memories/concept-example.md +90 -0
  29. data/examples/learning-assistant/example-memories/experience-example.md +66 -0
  30. data/examples/learning-assistant/example-memories/fact-example.md +76 -0
  31. data/examples/learning-assistant/example-memories/memory-index.md +78 -0
  32. data/examples/learning-assistant/example-memories/skill-example.md +168 -0
  33. data/examples/learning-assistant/learning_assistant.rb +34 -0
  34. data/examples/learning-assistant/learning_assistant.yml +20 -0
  35. data/examples/v2/dsl/01_basic.rb +44 -0
  36. data/examples/v2/dsl/02_core_parameters.rb +59 -0
  37. data/examples/v2/dsl/03_capabilities.rb +71 -0
  38. data/examples/v2/dsl/04_llm_parameters.rb +56 -0
  39. data/examples/v2/dsl/05_advanced_flags.rb +73 -0
  40. data/examples/v2/dsl/06_permissions.rb +80 -0
  41. data/examples/v2/dsl/07_mcp_server.rb +62 -0
  42. data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
  43. data/examples/v2/dsl/09_agent_hooks.rb +67 -0
  44. data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
  45. data/examples/v2/dsl/11_delegation.rb +60 -0
  46. data/examples/v2/dsl/12_complete_integration.rb +137 -0
  47. data/examples/v2/file_tools_swarm.yml +102 -0
  48. data/examples/v2/hooks/01_basic_hooks.rb +133 -0
  49. data/examples/v2/hooks/02_usage_tracking.rb +201 -0
  50. data/examples/v2/hooks/03_production_monitoring.rb +429 -0
  51. data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
  52. data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
  53. data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
  54. data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
  55. data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
  56. data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
  57. data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
  58. data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
  59. data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
  60. data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
  61. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
  62. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
  63. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
  64. data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
  65. data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
  66. data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
  67. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
  68. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
  69. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
  70. data/examples/v2/hooks/swarm_summary.sh +44 -0
  71. data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
  72. data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
  73. data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
  74. data/examples/v2/hooks/validate_bash.rb +59 -0
  75. data/examples/v2/multi_directory_permissions.yml +221 -0
  76. data/examples/v2/node_context_demo.rb +127 -0
  77. data/examples/v2/node_workflow.rb +173 -0
  78. data/examples/v2/path_resolution_demo.rb +216 -0
  79. data/examples/v2/simple-swarm-v2.rb +90 -0
  80. data/examples/v2/simple-swarm-v2.yml +62 -0
  81. data/examples/v2/swarm.yml +71 -0
  82. data/examples/v2/swarm_with_hooks.yml +61 -0
  83. data/examples/v2/swarm_with_hooks_simple.yml +25 -0
  84. data/examples/v2/think_tool_demo.rb +62 -0
  85. data/exe/swarm +6 -0
  86. data/lib/claude_swarm/claude_mcp_server.rb +0 -6
  87. data/lib/claude_swarm/cli.rb +10 -3
  88. data/lib/claude_swarm/commands/ps.rb +19 -20
  89. data/lib/claude_swarm/commands/show.rb +1 -1
  90. data/lib/claude_swarm/configuration.rb +10 -12
  91. data/lib/claude_swarm/mcp_generator.rb +10 -1
  92. data/lib/claude_swarm/orchestrator.rb +73 -49
  93. data/lib/claude_swarm/system_utils.rb +37 -11
  94. data/lib/claude_swarm/version.rb +1 -1
  95. data/lib/claude_swarm/worktree_manager.rb +1 -0
  96. data/lib/claude_swarm/yaml_loader.rb +22 -0
  97. data/lib/claude_swarm.rb +7 -3
  98. data/lib/swarm_cli/cli.rb +201 -0
  99. data/lib/swarm_cli/command_registry.rb +61 -0
  100. data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
  101. data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
  102. data/lib/swarm_cli/commands/migrate.rb +55 -0
  103. data/lib/swarm_cli/commands/run.rb +173 -0
  104. data/lib/swarm_cli/config_loader.rb +97 -0
  105. data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
  106. data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
  107. data/lib/swarm_cli/interactive_repl.rb +918 -0
  108. data/lib/swarm_cli/mcp_serve_options.rb +44 -0
  109. data/lib/swarm_cli/mcp_tools_options.rb +59 -0
  110. data/lib/swarm_cli/migrate_options.rb +54 -0
  111. data/lib/swarm_cli/migrator.rb +132 -0
  112. data/lib/swarm_cli/options.rb +151 -0
  113. data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
  114. data/lib/swarm_cli/ui/components/content_block.rb +120 -0
  115. data/lib/swarm_cli/ui/components/divider.rb +57 -0
  116. data/lib/swarm_cli/ui/components/panel.rb +62 -0
  117. data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
  118. data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
  119. data/lib/swarm_cli/ui/formatters/number.rb +58 -0
  120. data/lib/swarm_cli/ui/formatters/text.rb +77 -0
  121. data/lib/swarm_cli/ui/formatters/time.rb +73 -0
  122. data/lib/swarm_cli/ui/icons.rb +59 -0
  123. data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
  124. data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
  125. data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
  126. data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
  127. data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
  128. data/lib/swarm_cli/version.rb +5 -0
  129. data/lib/swarm_cli.rb +44 -0
  130. data/lib/swarm_memory/adapters/base.rb +141 -0
  131. data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
  132. data/lib/swarm_memory/chat_extension.rb +34 -0
  133. data/lib/swarm_memory/cli/commands.rb +306 -0
  134. data/lib/swarm_memory/core/entry.rb +37 -0
  135. data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
  136. data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
  137. data/lib/swarm_memory/core/path_normalizer.rb +75 -0
  138. data/lib/swarm_memory/core/semantic_index.rb +244 -0
  139. data/lib/swarm_memory/core/storage.rb +288 -0
  140. data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
  141. data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
  142. data/lib/swarm_memory/dsl/memory_config.rb +113 -0
  143. data/lib/swarm_memory/embeddings/embedder.rb +36 -0
  144. data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
  145. data/lib/swarm_memory/errors.rb +21 -0
  146. data/lib/swarm_memory/integration/cli_registration.rb +30 -0
  147. data/lib/swarm_memory/integration/configuration.rb +43 -0
  148. data/lib/swarm_memory/integration/registration.rb +31 -0
  149. data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
  150. data/lib/swarm_memory/optimization/analyzer.rb +244 -0
  151. data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
  152. data/lib/swarm_memory/prompts/memory.md.erb +109 -0
  153. data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
  154. data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
  155. data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
  156. data/lib/swarm_memory/search/semantic_search.rb +112 -0
  157. data/lib/swarm_memory/search/text_search.rb +42 -0
  158. data/lib/swarm_memory/search/text_similarity.rb +80 -0
  159. data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
  160. data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
  161. data/lib/swarm_memory/tools/load_skill.rb +313 -0
  162. data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
  163. data/lib/swarm_memory/tools/memory_delete.rb +99 -0
  164. data/lib/swarm_memory/tools/memory_edit.rb +185 -0
  165. data/lib/swarm_memory/tools/memory_glob.rb +160 -0
  166. data/lib/swarm_memory/tools/memory_grep.rb +247 -0
  167. data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
  168. data/lib/swarm_memory/tools/memory_read.rb +123 -0
  169. data/lib/swarm_memory/tools/memory_write.rb +231 -0
  170. data/lib/swarm_memory/utils.rb +50 -0
  171. data/lib/swarm_memory/version.rb +5 -0
  172. data/lib/swarm_memory.rb +166 -0
  173. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
  174. data/lib/swarm_sdk/agent/builder.rb +461 -0
  175. data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
  176. data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
  177. data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
  178. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
  179. data/lib/swarm_sdk/agent/chat.rb +1159 -0
  180. data/lib/swarm_sdk/agent/context.rb +112 -0
  181. data/lib/swarm_sdk/agent/context_manager.rb +309 -0
  182. data/lib/swarm_sdk/agent/definition.rb +556 -0
  183. data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
  184. data/lib/swarm_sdk/configuration.rb +296 -0
  185. data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
  186. data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
  187. data/lib/swarm_sdk/context_compactor.rb +340 -0
  188. data/lib/swarm_sdk/hooks/adapter.rb +359 -0
  189. data/lib/swarm_sdk/hooks/context.rb +197 -0
  190. data/lib/swarm_sdk/hooks/definition.rb +80 -0
  191. data/lib/swarm_sdk/hooks/error.rb +29 -0
  192. data/lib/swarm_sdk/hooks/executor.rb +146 -0
  193. data/lib/swarm_sdk/hooks/registry.rb +147 -0
  194. data/lib/swarm_sdk/hooks/result.rb +150 -0
  195. data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
  196. data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
  197. data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
  198. data/lib/swarm_sdk/log_collector.rb +51 -0
  199. data/lib/swarm_sdk/log_stream.rb +69 -0
  200. data/lib/swarm_sdk/markdown_parser.rb +75 -0
  201. data/lib/swarm_sdk/model_aliases.json +5 -0
  202. data/lib/swarm_sdk/models.json +1 -0
  203. data/lib/swarm_sdk/models.rb +120 -0
  204. data/lib/swarm_sdk/node/agent_config.rb +49 -0
  205. data/lib/swarm_sdk/node/builder.rb +439 -0
  206. data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
  207. data/lib/swarm_sdk/node_context.rb +170 -0
  208. data/lib/swarm_sdk/node_orchestrator.rb +384 -0
  209. data/lib/swarm_sdk/permissions/config.rb +239 -0
  210. data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
  211. data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
  212. data/lib/swarm_sdk/permissions/validator.rb +173 -0
  213. data/lib/swarm_sdk/permissions_builder.rb +122 -0
  214. data/lib/swarm_sdk/plugin.rb +147 -0
  215. data/lib/swarm_sdk/plugin_registry.rb +101 -0
  216. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
  217. data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
  218. data/lib/swarm_sdk/result.rb +97 -0
  219. data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
  220. data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
  221. data/lib/swarm_sdk/swarm/builder.rb +586 -0
  222. data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
  223. data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
  224. data/lib/swarm_sdk/swarm.rb +982 -0
  225. data/lib/swarm_sdk/tools/bash.rb +274 -0
  226. data/lib/swarm_sdk/tools/clock.rb +44 -0
  227. data/lib/swarm_sdk/tools/delegate.rb +164 -0
  228. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
  229. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
  230. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
  231. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
  232. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
  233. data/lib/swarm_sdk/tools/edit.rb +150 -0
  234. data/lib/swarm_sdk/tools/glob.rb +158 -0
  235. data/lib/swarm_sdk/tools/grep.rb +228 -0
  236. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
  237. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
  238. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
  239. data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
  240. data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
  241. data/lib/swarm_sdk/tools/read.rb +251 -0
  242. data/lib/swarm_sdk/tools/registry.rb +93 -0
  243. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
  244. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
  245. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
  246. data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
  247. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
  248. data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
  249. data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
  250. data/lib/swarm_sdk/tools/think.rb +95 -0
  251. data/lib/swarm_sdk/tools/todo_write.rb +216 -0
  252. data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
  253. data/lib/swarm_sdk/tools/write.rb +117 -0
  254. data/lib/swarm_sdk/utils.rb +50 -0
  255. data/lib/swarm_sdk/version.rb +5 -0
  256. data/lib/swarm_sdk.rb +157 -0
  257. data/llm.v2.txt +13407 -0
  258. data/rubocop/cop/security/no_reflection_methods.rb +47 -0
  259. data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
  260. data/swarm_cli.gemspec +57 -0
  261. data/swarm_memory.gemspec +28 -0
  262. data/swarm_sdk.gemspec +41 -0
  263. data/team.yml +1 -1
  264. data/team_full.yml +1875 -0
  265. data/{team_v2.yml → team_sdk.yml} +121 -52
  266. metadata +247 -4
  267. data/EXAMPLES.md +0 -164
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module Hooks
5
+ # Represents a tool call in the hooks system
6
+ #
7
+ # This is a simple value object that wraps tool call information
8
+ # from RubyLLM in a consistent, immutable format for hooks.
9
+ #
10
+ # @example Access tool call information in a hook
11
+ # swarm.add_callback(:pre_tool_use) do |context|
12
+ # puts "Tool: #{context.tool_call.name}"
13
+ # puts "Parameters: #{context.tool_call.parameters.inspect}"
14
+ # end
15
+ class ToolCall
16
+ attr_reader :id, :name, :parameters
17
+
18
+ # @param id [String] Unique identifier for this tool call
19
+ # @param name [String] Name of the tool being called
20
+ # @param parameters [Hash] Parameters passed to the tool
21
+ def initialize(id:, name:, parameters:)
22
+ @id = id
23
+ @name = name
24
+ @parameters = parameters
25
+ end
26
+
27
+ # Convert to hash representation
28
+ #
29
+ # @return [Hash] Hash with id, name, and parameters
30
+ def to_h
31
+ { id: @id, name: @name, parameters: @parameters }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module Hooks
5
+ # Represents the result of a tool execution
6
+ #
7
+ # This is a simple value object that wraps tool execution results
8
+ # in a consistent format for post-tool-use hooks.
9
+ #
10
+ # @example Access tool result in a hook
11
+ # swarm.add_callback(:post_tool_use) do |context|
12
+ # if context.tool_result.success?
13
+ # puts "Tool succeeded: #{context.tool_result.content}"
14
+ # else
15
+ # puts "Tool failed: #{context.tool_result.error}"
16
+ # end
17
+ # end
18
+ class ToolResult
19
+ attr_reader :tool_call_id, :tool_name, :content, :success, :error
20
+
21
+ # @param tool_call_id [String] ID of the tool call this result corresponds to
22
+ # @param tool_name [String] Name of the tool that was executed
23
+ # @param content [String, nil] Result content (if successful)
24
+ # @param success [Boolean] Whether the tool execution succeeded
25
+ # @param error [String, nil] Error message (if failed)
26
+ def initialize(tool_call_id:, tool_name:, content: nil, success: true, error: nil)
27
+ @tool_call_id = tool_call_id
28
+ @tool_name = tool_name
29
+ @content = content
30
+ @success = success
31
+ @error = error
32
+ end
33
+
34
+ # Check if the tool execution succeeded
35
+ #
36
+ # @return [Boolean] true if successful
37
+ def success?
38
+ @success
39
+ end
40
+
41
+ # Check if the tool execution failed
42
+ #
43
+ # @return [Boolean] true if failed
44
+ def failure?
45
+ !@success
46
+ end
47
+
48
+ # Convert to hash representation
49
+ #
50
+ # @return [Hash] Hash with all result attributes
51
+ def to_h
52
+ {
53
+ tool_call_id: @tool_call_id,
54
+ tool_name: @tool_name,
55
+ content: @content,
56
+ success: @success,
57
+ error: @error,
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ # LogCollector manages subscriber callbacks for log events.
5
+ #
6
+ # This module acts as an emitter implementation that forwards events
7
+ # to user-registered callbacks. It's designed to be set as the LogStream
8
+ # emitter during swarm execution.
9
+ #
10
+ # ## Usage
11
+ #
12
+ # # Register a callback (before execution starts)
13
+ # LogCollector.on_log do |event|
14
+ # puts JSON.generate(event)
15
+ # end
16
+ #
17
+ # # During execution, LogStream calls emit
18
+ # LogCollector.emit(type: "user_prompt", agent: :backend)
19
+ #
20
+ # # After execution, reset for next use
21
+ # LogCollector.reset!
22
+ #
23
+ module LogCollector
24
+ class << self
25
+ # Register a callback to receive log events
26
+ #
27
+ # @yield [Hash] Log event entry
28
+ def on_log(&block)
29
+ @callbacks ||= []
30
+ @callbacks << block
31
+ end
32
+
33
+ # Emit an event to all registered callbacks
34
+ #
35
+ # @param entry [Hash] Log event entry
36
+ # @return [void]
37
+ def emit(entry)
38
+ Array(@callbacks).each do |callback|
39
+ callback.call(entry)
40
+ end
41
+ end
42
+
43
+ # Reset the collector (clears callbacks for next execution)
44
+ #
45
+ # @return [void]
46
+ def reset!
47
+ @callbacks = []
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ # LogStream provides a module-level singleton for emitting log events.
5
+ #
6
+ # This allows any component (tools, providers, agents) to emit structured
7
+ # log events without needing references to logger instances.
8
+ #
9
+ # ## Usage
10
+ #
11
+ # # Emit an event from anywhere in the SDK
12
+ # LogStream.emit(
13
+ # type: "user_prompt",
14
+ # agent: :backend,
15
+ # model: "claude-sonnet-4",
16
+ # message_count: 5
17
+ # )
18
+ #
19
+ # ## Fiber Safety
20
+ #
21
+ # LogStream is fiber-safe when following this pattern:
22
+ # 1. Set emitter BEFORE starting Async execution
23
+ # 2. During Async execution, only emit() (reads emitter)
24
+ # 3. Each event includes agent context for identification
25
+ #
26
+ # ## Testing
27
+ #
28
+ # # Inject a test emitter
29
+ # LogStream.emitter = TestEmitter.new
30
+ # # ... run tests ...
31
+ # LogStream.reset!
32
+ #
33
+ module LogStream
34
+ class << self
35
+ # Emit a log event
36
+ #
37
+ # Adds timestamp and forwards to the registered emitter.
38
+ #
39
+ # @param data [Hash] Event data (type, agent, and event-specific fields)
40
+ # @return [void]
41
+ def emit(**data)
42
+ return unless @emitter
43
+
44
+ entry = data.merge(timestamp: Time.now.utc.iso8601).compact
45
+
46
+ @emitter.emit(entry)
47
+ end
48
+
49
+ # Set the emitter (for dependency injection in tests)
50
+ #
51
+ # @param emitter [#emit] Object responding to emit(Hash)
52
+ attr_accessor :emitter
53
+
54
+ # Reset the emitter (for test cleanup)
55
+ #
56
+ # @return [void]
57
+ def reset!
58
+ @emitter = nil
59
+ end
60
+
61
+ # Check if logging is enabled
62
+ #
63
+ # @return [Boolean] true if an emitter is configured
64
+ def enabled?
65
+ !@emitter.nil?
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
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.
11
+ class MarkdownParser
12
+ FRONTMATTER_PATTERN = /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
13
+
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
24
+ def parse(content, agent_name = nil)
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
39
+ end
40
+ end
41
+
42
+ def initialize(content, agent_name = nil)
43
+ @content = content
44
+ @agent_name = agent_name
45
+ end
46
+
47
+ def parse
48
+ if @content =~ FRONTMATTER_PATTERN
49
+ frontmatter_yaml = Regexp.last_match(1)
50
+ prompt_content = Regexp.last_match(2).strip
51
+
52
+ frontmatter = YAML.safe_load(frontmatter_yaml, permitted_classes: [Symbol], aliases: true)
53
+
54
+ unless frontmatter.is_a?(Hash)
55
+ raise ConfigurationError, "Invalid frontmatter format in agent definition"
56
+ end
57
+
58
+ # Symbolize keys for AgentDefinition
59
+ config = Utils.symbolize_keys(frontmatter).merge(system_prompt: prompt_content)
60
+
61
+ name = @agent_name || frontmatter["name"]
62
+ unless name
63
+ raise ConfigurationError, "Agent definition must include 'name' in frontmatter or be specified externally"
64
+ end
65
+
66
+ # Convert name to symbol
67
+ name = name.to_sym
68
+
69
+ Agent::Definition.new(name, config)
70
+ else
71
+ raise ConfigurationError, "Invalid Markdown agent definition format. Expected YAML frontmatter followed by prompt content."
72
+ end
73
+ end
74
+ end
75
+ end
@@ -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