ruby_llm-agents 0.4.0 → 1.0.0.beta.1

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 (208) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +225 -34
  3. data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
  4. data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +214 -0
  5. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
  6. data/app/controllers/ruby_llm/agents/{settings_controller.rb → system_config_controller.rb} +3 -3
  7. data/app/controllers/ruby_llm/agents/tenants_controller.rb +109 -0
  8. data/app/controllers/ruby_llm/agents/workflows_controller.rb +355 -0
  9. data/app/helpers/ruby_llm/agents/application_helper.rb +25 -0
  10. data/app/models/ruby_llm/agents/api_configuration.rb +386 -0
  11. data/app/models/ruby_llm/agents/execution.rb +3 -0
  12. data/app/models/ruby_llm/agents/tenant_budget.rb +112 -14
  13. data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
  14. data/app/views/layouts/ruby_llm/agents/application.html.erb +5 -30
  15. data/app/views/ruby_llm/agents/agents/_agent.html.erb +13 -1
  16. data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +235 -0
  17. data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +70 -0
  18. data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +152 -0
  19. data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +63 -0
  20. data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +108 -0
  21. data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +91 -0
  22. data/app/views/ruby_llm/agents/agents/_workflow.html.erb +1 -1
  23. data/app/views/ruby_llm/agents/agents/index.html.erb +74 -9
  24. data/app/views/ruby_llm/agents/agents/show.html.erb +18 -378
  25. data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +34 -0
  26. data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +288 -0
  27. data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +95 -0
  28. data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +97 -0
  29. data/app/views/ruby_llm/agents/api_configurations/show.html.erb +211 -0
  30. data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +179 -0
  31. data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +1 -1
  32. data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
  33. data/app/views/ruby_llm/agents/executions/show.html.erb +98 -0
  34. data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
  35. data/app/views/ruby_llm/agents/{settings → system_config}/show.html.erb +1 -1
  36. data/app/views/ruby_llm/agents/tenants/_form.html.erb +150 -0
  37. data/app/views/ruby_llm/agents/tenants/edit.html.erb +13 -0
  38. data/app/views/ruby_llm/agents/tenants/index.html.erb +129 -0
  39. data/app/views/ruby_llm/agents/tenants/show.html.erb +374 -0
  40. data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +236 -0
  41. data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +76 -0
  42. data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +74 -0
  43. data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +108 -0
  44. data/app/views/ruby_llm/agents/workflows/show.html.erb +442 -0
  45. data/config/routes.rb +13 -1
  46. data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
  47. data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +100 -0
  48. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +110 -0
  49. data/lib/generators/ruby_llm_agents/embedder_generator.rb +107 -0
  50. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +115 -0
  51. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +108 -0
  52. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +116 -0
  53. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +178 -0
  54. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +109 -0
  55. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +103 -0
  56. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +102 -0
  57. data/lib/generators/ruby_llm_agents/install_generator.rb +76 -4
  58. data/lib/generators/ruby_llm_agents/restructure_generator.rb +292 -0
  59. data/lib/generators/ruby_llm_agents/speaker_generator.rb +121 -0
  60. data/lib/generators/ruby_llm_agents/templates/add_execution_type_migration.rb.tt +8 -0
  61. data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +99 -84
  62. data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +42 -40
  63. data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +26 -0
  64. data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +50 -0
  65. data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +26 -0
  66. data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +20 -0
  67. data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +38 -0
  68. data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +139 -0
  69. data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +21 -0
  70. data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +20 -0
  71. data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +20 -0
  72. data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +49 -0
  73. data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +53 -0
  74. data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +44 -0
  75. data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +90 -0
  76. data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +41 -0
  77. data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +45 -0
  78. data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +35 -0
  79. data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +47 -0
  80. data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +50 -0
  81. data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +44 -0
  82. data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +38 -0
  83. data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +33 -0
  84. data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +228 -0
  85. data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +131 -0
  86. data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +255 -0
  87. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +120 -0
  88. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +102 -0
  89. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +282 -0
  90. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +228 -0
  91. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +120 -0
  92. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +110 -0
  93. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +120 -0
  94. data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +212 -0
  95. data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +227 -0
  96. data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +251 -0
  97. data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +300 -0
  98. data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +56 -0
  99. data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +51 -0
  100. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +107 -0
  101. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +152 -1
  102. data/lib/ruby_llm/agents/audio/speaker.rb +553 -0
  103. data/lib/ruby_llm/agents/audio/transcriber.rb +669 -0
  104. data/lib/ruby_llm/agents/base_agent.rb +675 -0
  105. data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +181 -0
  106. data/lib/ruby_llm/agents/core/base/moderation_execution.rb +274 -0
  107. data/lib/ruby_llm/agents/core/base.rb +135 -0
  108. data/lib/ruby_llm/agents/core/configuration.rb +981 -0
  109. data/lib/ruby_llm/agents/core/errors.rb +150 -0
  110. data/lib/ruby_llm/agents/{instrumentation.rb → core/instrumentation.rb} +93 -4
  111. data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
  112. data/lib/ruby_llm/agents/core/resolved_config.rb +348 -0
  113. data/lib/ruby_llm/agents/{version.rb → core/version.rb} +1 -1
  114. data/lib/ruby_llm/agents/dsl/base.rb +110 -0
  115. data/lib/ruby_llm/agents/dsl/caching.rb +142 -0
  116. data/lib/ruby_llm/agents/dsl/reliability.rb +307 -0
  117. data/lib/ruby_llm/agents/dsl.rb +41 -0
  118. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +130 -0
  119. data/lib/ruby_llm/agents/image/analyzer/execution.rb +402 -0
  120. data/lib/ruby_llm/agents/image/analyzer.rb +90 -0
  121. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +154 -0
  122. data/lib/ruby_llm/agents/image/background_remover/execution.rb +240 -0
  123. data/lib/ruby_llm/agents/image/background_remover.rb +89 -0
  124. data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +91 -0
  125. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +165 -0
  126. data/lib/ruby_llm/agents/image/editor/dsl.rb +56 -0
  127. data/lib/ruby_llm/agents/image/editor/execution.rb +207 -0
  128. data/lib/ruby_llm/agents/image/editor.rb +92 -0
  129. data/lib/ruby_llm/agents/image/generator/active_storage_support.rb +127 -0
  130. data/lib/ruby_llm/agents/image/generator/content_policy.rb +95 -0
  131. data/lib/ruby_llm/agents/image/generator/pricing.rb +353 -0
  132. data/lib/ruby_llm/agents/image/generator/templates.rb +124 -0
  133. data/lib/ruby_llm/agents/image/generator.rb +455 -0
  134. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +213 -0
  135. data/lib/ruby_llm/agents/image/pipeline/execution.rb +382 -0
  136. data/lib/ruby_llm/agents/image/pipeline.rb +97 -0
  137. data/lib/ruby_llm/agents/image/transformer/dsl.rb +148 -0
  138. data/lib/ruby_llm/agents/image/transformer/execution.rb +223 -0
  139. data/lib/ruby_llm/agents/image/transformer.rb +95 -0
  140. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +83 -0
  141. data/lib/ruby_llm/agents/image/upscaler/execution.rb +219 -0
  142. data/lib/ruby_llm/agents/image/upscaler.rb +81 -0
  143. data/lib/ruby_llm/agents/image/variator/dsl.rb +62 -0
  144. data/lib/ruby_llm/agents/image/variator/execution.rb +189 -0
  145. data/lib/ruby_llm/agents/image/variator.rb +80 -0
  146. data/lib/ruby_llm/agents/{alert_manager.rb → infrastructure/alert_manager.rb} +17 -22
  147. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +145 -0
  148. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +149 -0
  149. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +68 -0
  150. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +279 -0
  151. data/lib/ruby_llm/agents/infrastructure/budget_tracker.rb +275 -0
  152. data/lib/ruby_llm/agents/{execution_logger_job.rb → infrastructure/execution_logger_job.rb} +17 -1
  153. data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/executor.rb +2 -1
  154. data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/retry_strategy.rb +9 -3
  155. data/lib/ruby_llm/agents/{reliability.rb → infrastructure/reliability.rb} +11 -21
  156. data/lib/ruby_llm/agents/pipeline/builder.rb +215 -0
  157. data/lib/ruby_llm/agents/pipeline/context.rb +255 -0
  158. data/lib/ruby_llm/agents/pipeline/executor.rb +86 -0
  159. data/lib/ruby_llm/agents/pipeline/middleware/base.rb +124 -0
  160. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +95 -0
  161. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +171 -0
  162. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +415 -0
  163. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +276 -0
  164. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +196 -0
  165. data/lib/ruby_llm/agents/pipeline.rb +68 -0
  166. data/lib/ruby_llm/agents/{engine.rb → rails/engine.rb} +79 -10
  167. data/lib/ruby_llm/agents/results/background_removal_result.rb +286 -0
  168. data/lib/ruby_llm/agents/{result.rb → results/base.rb} +73 -1
  169. data/lib/ruby_llm/agents/results/embedding_result.rb +243 -0
  170. data/lib/ruby_llm/agents/results/image_analysis_result.rb +314 -0
  171. data/lib/ruby_llm/agents/results/image_edit_result.rb +250 -0
  172. data/lib/ruby_llm/agents/results/image_generation_result.rb +346 -0
  173. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +399 -0
  174. data/lib/ruby_llm/agents/results/image_transform_result.rb +251 -0
  175. data/lib/ruby_llm/agents/results/image_upscale_result.rb +255 -0
  176. data/lib/ruby_llm/agents/results/image_variation_result.rb +237 -0
  177. data/lib/ruby_llm/agents/results/moderation_result.rb +158 -0
  178. data/lib/ruby_llm/agents/results/speech_result.rb +338 -0
  179. data/lib/ruby_llm/agents/results/transcription_result.rb +408 -0
  180. data/lib/ruby_llm/agents/text/embedder.rb +444 -0
  181. data/lib/ruby_llm/agents/text/moderator.rb +237 -0
  182. data/lib/ruby_llm/agents/workflow/async.rb +220 -0
  183. data/lib/ruby_llm/agents/workflow/async_executor.rb +156 -0
  184. data/lib/ruby_llm/agents/{workflow.rb → workflow/orchestrator.rb} +6 -5
  185. data/lib/ruby_llm/agents/workflow/parallel.rb +34 -17
  186. data/lib/ruby_llm/agents/workflow/thread_pool.rb +185 -0
  187. data/lib/ruby_llm/agents.rb +86 -20
  188. metadata +189 -35
  189. data/lib/ruby_llm/agents/base/caching.rb +0 -40
  190. data/lib/ruby_llm/agents/base/cost_calculation.rb +0 -105
  191. data/lib/ruby_llm/agents/base/dsl.rb +0 -324
  192. data/lib/ruby_llm/agents/base/execution.rb +0 -283
  193. data/lib/ruby_llm/agents/base/reliability_dsl.rb +0 -82
  194. data/lib/ruby_llm/agents/base/reliability_execution.rb +0 -136
  195. data/lib/ruby_llm/agents/base/response_building.rb +0 -86
  196. data/lib/ruby_llm/agents/base/tool_tracking.rb +0 -57
  197. data/lib/ruby_llm/agents/base.rb +0 -209
  198. data/lib/ruby_llm/agents/budget_tracker.rb +0 -471
  199. data/lib/ruby_llm/agents/configuration.rb +0 -357
  200. /data/lib/ruby_llm/agents/{deprecations.rb → core/deprecations.rb} +0 -0
  201. /data/lib/ruby_llm/agents/{inflections.rb → core/inflections.rb} +0 -0
  202. /data/lib/ruby_llm/agents/{attempt_tracker.rb → infrastructure/attempt_tracker.rb} +0 -0
  203. /data/lib/ruby_llm/agents/{cache_helper.rb → infrastructure/cache_helper.rb} +0 -0
  204. /data/lib/ruby_llm/agents/{circuit_breaker.rb → infrastructure/circuit_breaker.rb} +0 -0
  205. /data/lib/ruby_llm/agents/{redactor.rb → infrastructure/redactor.rb} +0 -0
  206. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/breaker_manager.rb +0 -0
  207. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/execution_constraints.rb +0 -0
  208. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/fallback_routing.rb +0 -0
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RubyLlmAgents
6
+ # ImagePipeline generator for creating new image pipelines
7
+ #
8
+ # Usage:
9
+ # rails generate ruby_llm_agents:image_pipeline Product
10
+ # rails generate ruby_llm_agents:image_pipeline Ecommerce --steps generate,upscale,remove_background
11
+ # rails generate ruby_llm_agents:image_pipeline Content --steps generate,analyze
12
+ # rails generate ruby_llm_agents:image_pipeline Product --root=ai
13
+ #
14
+ # This will create:
15
+ # - app/{root}/image/pipelines/product_pipeline.rb
16
+ #
17
+ class ImagePipelineGenerator < ::Rails::Generators::NamedBase
18
+ source_root File.expand_path("templates", __dir__)
19
+
20
+ class_option :steps, type: :string, default: "generate,upscale",
21
+ desc: "Pipeline steps (comma-separated: generate,upscale,transform,analyze,remove_background)"
22
+ class_option :stop_on_error, type: :boolean, default: true,
23
+ desc: "Stop pipeline on first error"
24
+ class_option :cache, type: :string, default: nil,
25
+ desc: "Cache TTL (e.g., '1.hour', '1.day')"
26
+ class_option :root,
27
+ type: :string,
28
+ default: nil,
29
+ desc: "Root directory name (default: uses config or 'llm')"
30
+ class_option :namespace,
31
+ type: :string,
32
+ default: nil,
33
+ desc: "Root namespace (default: camelized root or config)"
34
+
35
+ def ensure_base_class_and_skill_file
36
+ @root_namespace = root_namespace
37
+ @image_namespace = "#{root_namespace}::Image"
38
+ pipelines_dir = "app/#{root_directory}/image/pipelines"
39
+
40
+ # Create directory if needed
41
+ empty_directory pipelines_dir
42
+
43
+ # Create base class if it doesn't exist
44
+ base_class_path = "#{pipelines_dir}/application_image_pipeline.rb"
45
+ unless File.exist?(File.join(destination_root, base_class_path))
46
+ template "application_image_pipeline.rb.tt", base_class_path
47
+ end
48
+
49
+ # Create skill file if it doesn't exist
50
+ skill_file_path = "#{pipelines_dir}/IMAGE_PIPELINES.md"
51
+ unless File.exist?(File.join(destination_root, skill_file_path))
52
+ template "skills/IMAGE_PIPELINES.md.tt", skill_file_path
53
+ end
54
+ end
55
+
56
+ def create_image_pipeline_file
57
+ @root_namespace = root_namespace
58
+ @image_namespace = "#{root_namespace}::Image"
59
+ pipeline_path = name.underscore
60
+ template "image_pipeline.rb.tt", "app/#{root_directory}/image/pipelines/#{pipeline_path}_pipeline.rb"
61
+ end
62
+
63
+ def create_step_classes
64
+ # Create stub classes for referenced steps if they don't exist
65
+ parsed_steps.each do |step|
66
+ create_step_stub(step) if should_create_stub?(step)
67
+ end
68
+ end
69
+
70
+ def show_usage
71
+ pipeline_class_name = name.split("/").map(&:camelize).join("::")
72
+ full_class_name = "#{root_namespace}::Image::#{pipeline_class_name}Pipeline"
73
+ say ""
74
+ say "Image pipeline #{full_class_name} created!", :green
75
+ say ""
76
+ say "Usage:"
77
+ say " # Run the pipeline"
78
+ say " result = #{full_class_name}.call(prompt: 'Product photo')"
79
+ say " result.final_image # => The processed image"
80
+ say " result.total_cost # => Combined cost of all steps"
81
+ say " result.step_count # => Number of steps executed"
82
+ say ""
83
+ say " # Access individual step results"
84
+ say " result.step(:generate) # => ImageGenerationResult"
85
+ say " result.step(:upscale) # => ImageUpscaleResult"
86
+ say " result.analysis # => ImageAnalysisResult (if analyzer step)"
87
+ say ""
88
+ say " # Save the final image"
89
+ say " result.save('output.png')"
90
+ say ""
91
+ say " # With tenant tracking"
92
+ say " result = #{full_class_name}.call("
93
+ say " prompt: 'Product photo',"
94
+ say " tenant: current_organization"
95
+ say " )"
96
+ say ""
97
+ end
98
+
99
+ private
100
+
101
+ def root_directory
102
+ @root_directory ||= options[:root] || RubyLLM::Agents.configuration.root_directory
103
+ end
104
+
105
+ def root_namespace
106
+ @root_namespace ||= options[:namespace] || camelize(root_directory)
107
+ end
108
+
109
+ def camelize(str)
110
+ return "AI" if str.downcase == "ai"
111
+ return "ML" if str.downcase == "ml"
112
+ return "LLM" if str.downcase == "llm"
113
+ str.split(/[-_]/).map(&:capitalize).join
114
+ end
115
+
116
+ def parsed_steps
117
+ options[:steps].to_s.split(",").map(&:strip).map(&:to_sym)
118
+ end
119
+
120
+ def should_create_stub?(step)
121
+ case step
122
+ when :generate
123
+ !File.exist?("app/#{root_directory}/image/generators/#{name.underscore}_generator.rb")
124
+ when :upscale
125
+ !File.exist?("app/#{root_directory}/image/upscalers/#{name.underscore}_upscaler.rb")
126
+ when :transform
127
+ !File.exist?("app/#{root_directory}/image/transformers/#{name.underscore}_transformer.rb")
128
+ when :analyze
129
+ !File.exist?("app/#{root_directory}/image/analyzers/#{name.underscore}_analyzer.rb")
130
+ when :remove_background
131
+ !File.exist?("app/#{root_directory}/image/background_removers/#{name.underscore}_background_remover.rb")
132
+ else
133
+ false
134
+ end
135
+ end
136
+
137
+ def create_step_stub(step)
138
+ # Just show a note - don't auto-create stubs
139
+ case step
140
+ when :generate
141
+ say " Note: You may want to create #{name}Generator with:", :yellow
142
+ say " rails generate ruby_llm_agents:image_generator #{name}"
143
+ when :upscale
144
+ say " Note: You may want to create #{name}Upscaler with:", :yellow
145
+ say " rails generate ruby_llm_agents:image_upscaler #{name}"
146
+ when :transform
147
+ say " Note: You may want to create #{name}Transformer with:", :yellow
148
+ say " rails generate ruby_llm_agents:image_transformer #{name}"
149
+ when :analyze
150
+ say " Note: You may want to create #{name}Analyzer with:", :yellow
151
+ say " rails generate ruby_llm_agents:image_analyzer #{name}"
152
+ when :remove_background
153
+ say " Note: You may want to create #{name}BackgroundRemover with:", :yellow
154
+ say " rails generate ruby_llm_agents:background_remover #{name}"
155
+ end
156
+ end
157
+
158
+ def step_classes
159
+ @step_classes ||= parsed_steps.map do |step|
160
+ class_base = name.split("/").map(&:camelize).join("::")
161
+ case step
162
+ when :generate
163
+ { step: step, type: :generator, class_name: "#{@image_namespace}::#{class_base}Generator" }
164
+ when :upscale
165
+ { step: step, type: :upscaler, class_name: "#{@image_namespace}::#{class_base}Upscaler" }
166
+ when :transform
167
+ { step: step, type: :transformer, class_name: "#{@image_namespace}::#{class_base}Transformer" }
168
+ when :analyze
169
+ { step: step, type: :analyzer, class_name: "#{@image_namespace}::#{class_base}Analyzer" }
170
+ when :remove_background
171
+ { step: step, type: :remover, class_name: "#{@image_namespace}::#{class_base}BackgroundRemover" }
172
+ else
173
+ { step: step, type: step, class_name: "#{@image_namespace}::#{class_base}#{step.to_s.camelize}" }
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RubyLlmAgents
6
+ # ImageTransformer generator for creating new image transformers
7
+ #
8
+ # Usage:
9
+ # rails generate ruby_llm_agents:image_transformer Anime
10
+ # rails generate ruby_llm_agents:image_transformer Watercolor --model sdxl --strength 0.8
11
+ # rails generate ruby_llm_agents:image_transformer Oil --template "oil painting, {prompt}"
12
+ # rails generate ruby_llm_agents:image_transformer Anime --root=ai
13
+ #
14
+ # This will create:
15
+ # - app/{root}/image/transformers/anime_transformer.rb
16
+ #
17
+ class ImageTransformerGenerator < ::Rails::Generators::NamedBase
18
+ source_root File.expand_path("templates", __dir__)
19
+
20
+ class_option :model, type: :string, default: "sdxl",
21
+ desc: "The image model to use"
22
+ class_option :size, type: :string, default: "1024x1024",
23
+ desc: "Output image size (e.g., 1024x1024)"
24
+ class_option :strength, type: :string, default: "0.75",
25
+ desc: "Transformation strength (0.0-1.0)"
26
+ class_option :template, type: :string, default: nil,
27
+ desc: "Prompt template (use {prompt} as placeholder)"
28
+ class_option :content_policy, type: :string, default: "standard",
29
+ desc: "Content policy level (none, standard, moderate, strict)"
30
+ class_option :cache, type: :string, default: nil,
31
+ desc: "Cache TTL (e.g., '1.hour', '1.day')"
32
+ class_option :root,
33
+ type: :string,
34
+ default: nil,
35
+ desc: "Root directory name (default: uses config or 'llm')"
36
+ class_option :namespace,
37
+ type: :string,
38
+ default: nil,
39
+ desc: "Root namespace (default: camelized root or config)"
40
+
41
+ def ensure_base_class_and_skill_file
42
+ @root_namespace = root_namespace
43
+ @image_namespace = "#{root_namespace}::Image"
44
+ transformers_dir = "app/#{root_directory}/image/transformers"
45
+
46
+ # Create directory if needed
47
+ empty_directory transformers_dir
48
+
49
+ # Create base class if it doesn't exist
50
+ base_class_path = "#{transformers_dir}/application_image_transformer.rb"
51
+ unless File.exist?(File.join(destination_root, base_class_path))
52
+ template "application_image_transformer.rb.tt", base_class_path
53
+ end
54
+
55
+ # Create skill file if it doesn't exist
56
+ skill_file_path = "#{transformers_dir}/IMAGE_TRANSFORMERS.md"
57
+ unless File.exist?(File.join(destination_root, skill_file_path))
58
+ template "skills/IMAGE_TRANSFORMERS.md.tt", skill_file_path
59
+ end
60
+ end
61
+
62
+ def create_image_transformer_file
63
+ @root_namespace = root_namespace
64
+ @image_namespace = "#{root_namespace}::Image"
65
+ transformer_path = name.underscore
66
+ template "image_transformer.rb.tt", "app/#{root_directory}/image/transformers/#{transformer_path}_transformer.rb"
67
+ end
68
+
69
+ def show_usage
70
+ transformer_class_name = name.split("/").map(&:camelize).join("::")
71
+ full_class_name = "#{root_namespace}::Image::#{transformer_class_name}Transformer"
72
+ say ""
73
+ say "Image transformer #{full_class_name} created!", :green
74
+ say ""
75
+ say "Usage:"
76
+ say " # Transform an image with a style"
77
+ say " result = #{full_class_name}.call("
78
+ say " image: 'photo.jpg',"
79
+ say " prompt: 'portrait of a person'"
80
+ say " )"
81
+ say " result.url # => 'https://...'"
82
+ say ""
83
+ say " # Override strength at runtime"
84
+ say " result = #{full_class_name}.call("
85
+ say " image: 'photo.jpg',"
86
+ say " prompt: 'detailed portrait',"
87
+ say " strength: 0.9"
88
+ say " )"
89
+ say ""
90
+ end
91
+
92
+ private
93
+
94
+ def root_directory
95
+ @root_directory ||= options[:root] || RubyLLM::Agents.configuration.root_directory
96
+ end
97
+
98
+ def root_namespace
99
+ @root_namespace ||= options[:namespace] || camelize(root_directory)
100
+ end
101
+
102
+ def camelize(str)
103
+ return "AI" if str.downcase == "ai"
104
+ return "ML" if str.downcase == "ml"
105
+ return "LLM" if str.downcase == "llm"
106
+ str.split(/[-_]/).map(&:capitalize).join
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RubyLlmAgents
6
+ # ImageUpscaler generator for creating new image upscalers
7
+ #
8
+ # Usage:
9
+ # rails generate ruby_llm_agents:image_upscaler Photo
10
+ # rails generate ruby_llm_agents:image_upscaler Portrait --model real-esrgan --scale 4
11
+ # rails generate ruby_llm_agents:image_upscaler Face --face_enhance
12
+ # rails generate ruby_llm_agents:image_upscaler Photo --root=ai
13
+ #
14
+ # This will create:
15
+ # - app/{root}/image/upscalers/photo_upscaler.rb
16
+ #
17
+ class ImageUpscalerGenerator < ::Rails::Generators::NamedBase
18
+ source_root File.expand_path("templates", __dir__)
19
+
20
+ class_option :model, type: :string, default: "real-esrgan",
21
+ desc: "The upscaling model to use"
22
+ class_option :scale, type: :string, default: "4",
23
+ desc: "Upscale factor (2, 4, or 8)"
24
+ class_option :face_enhance, type: :boolean, default: false,
25
+ desc: "Enable face enhancement"
26
+ class_option :cache, type: :string, default: nil,
27
+ desc: "Cache TTL (e.g., '1.hour', '1.day')"
28
+ class_option :root,
29
+ type: :string,
30
+ default: nil,
31
+ desc: "Root directory name (default: uses config or 'llm')"
32
+ class_option :namespace,
33
+ type: :string,
34
+ default: nil,
35
+ desc: "Root namespace (default: camelized root or config)"
36
+
37
+ def ensure_base_class_and_skill_file
38
+ @root_namespace = root_namespace
39
+ @image_namespace = "#{root_namespace}::Image"
40
+ upscalers_dir = "app/#{root_directory}/image/upscalers"
41
+
42
+ # Create directory if needed
43
+ empty_directory upscalers_dir
44
+
45
+ # Create base class if it doesn't exist
46
+ base_class_path = "#{upscalers_dir}/application_image_upscaler.rb"
47
+ unless File.exist?(File.join(destination_root, base_class_path))
48
+ template "application_image_upscaler.rb.tt", base_class_path
49
+ end
50
+
51
+ # Create skill file if it doesn't exist
52
+ skill_file_path = "#{upscalers_dir}/IMAGE_UPSCALERS.md"
53
+ unless File.exist?(File.join(destination_root, skill_file_path))
54
+ template "skills/IMAGE_UPSCALERS.md.tt", skill_file_path
55
+ end
56
+ end
57
+
58
+ def create_image_upscaler_file
59
+ @root_namespace = root_namespace
60
+ @image_namespace = "#{root_namespace}::Image"
61
+ upscaler_path = name.underscore
62
+ template "image_upscaler.rb.tt", "app/#{root_directory}/image/upscalers/#{upscaler_path}_upscaler.rb"
63
+ end
64
+
65
+ def show_usage
66
+ upscaler_class_name = name.split("/").map(&:camelize).join("::")
67
+ full_class_name = "#{root_namespace}::Image::#{upscaler_class_name}Upscaler"
68
+ say ""
69
+ say "Image upscaler #{full_class_name} created!", :green
70
+ say ""
71
+ say "Usage:"
72
+ say " # Upscale an image"
73
+ say " result = #{full_class_name}.call(image: 'low_res.jpg')"
74
+ say " result.url # => 'https://...'"
75
+ say " result.output_size # => '4096x4096'"
76
+ say ""
77
+ say " # Override settings at runtime"
78
+ say " result = #{full_class_name}.call("
79
+ say " image: 'photo.jpg',"
80
+ say " scale: 8,"
81
+ say " face_enhance: true"
82
+ say " )"
83
+ say ""
84
+ end
85
+
86
+ private
87
+
88
+ def root_directory
89
+ @root_directory ||= options[:root] || RubyLLM::Agents.configuration.root_directory
90
+ end
91
+
92
+ def root_namespace
93
+ @root_namespace ||= options[:namespace] || camelize(root_directory)
94
+ end
95
+
96
+ def camelize(str)
97
+ return "AI" if str.downcase == "ai"
98
+ return "ML" if str.downcase == "ml"
99
+ return "LLM" if str.downcase == "llm"
100
+ str.split(/[-_]/).map(&:capitalize).join
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RubyLlmAgents
6
+ # ImageVariator generator for creating new image variators
7
+ #
8
+ # Usage:
9
+ # rails generate ruby_llm_agents:image_variator Logo
10
+ # rails generate ruby_llm_agents:image_variator Product --model gpt-image-1 --size 1024x1024
11
+ # rails generate ruby_llm_agents:image_variator Avatar --variation_strength 0.3
12
+ # rails generate ruby_llm_agents:image_variator Logo --root=ai
13
+ #
14
+ # This will create:
15
+ # - app/{root}/image/variators/logo_variator.rb
16
+ #
17
+ class ImageVariatorGenerator < ::Rails::Generators::NamedBase
18
+ source_root File.expand_path("templates", __dir__)
19
+
20
+ class_option :model, type: :string, default: "gpt-image-1",
21
+ desc: "The image model to use"
22
+ class_option :size, type: :string, default: "1024x1024",
23
+ desc: "Output image size (e.g., 1024x1024)"
24
+ class_option :variation_strength, type: :string, default: "0.5",
25
+ desc: "Variation strength (0.0-1.0)"
26
+ class_option :cache, type: :string, default: nil,
27
+ desc: "Cache TTL (e.g., '1.hour', '1.day')"
28
+ class_option :root,
29
+ type: :string,
30
+ default: nil,
31
+ desc: "Root directory name (default: uses config or 'llm')"
32
+ class_option :namespace,
33
+ type: :string,
34
+ default: nil,
35
+ desc: "Root namespace (default: camelized root or config)"
36
+
37
+ def ensure_base_class_and_skill_file
38
+ @root_namespace = root_namespace
39
+ @image_namespace = "#{root_namespace}::Image"
40
+ variators_dir = "app/#{root_directory}/image/variators"
41
+
42
+ # Create directory if needed
43
+ empty_directory variators_dir
44
+
45
+ # Create base class if it doesn't exist
46
+ base_class_path = "#{variators_dir}/application_image_variator.rb"
47
+ unless File.exist?(File.join(destination_root, base_class_path))
48
+ template "application_image_variator.rb.tt", base_class_path
49
+ end
50
+
51
+ # Create skill file if it doesn't exist
52
+ skill_file_path = "#{variators_dir}/IMAGE_VARIATORS.md"
53
+ unless File.exist?(File.join(destination_root, skill_file_path))
54
+ template "skills/IMAGE_VARIATORS.md.tt", skill_file_path
55
+ end
56
+ end
57
+
58
+ def create_image_variator_file
59
+ @root_namespace = root_namespace
60
+ @image_namespace = "#{root_namespace}::Image"
61
+ variator_path = name.underscore
62
+ template "image_variator.rb.tt", "app/#{root_directory}/image/variators/#{variator_path}_variator.rb"
63
+ end
64
+
65
+ def show_usage
66
+ variator_class_name = name.split("/").map(&:camelize).join("::")
67
+ full_class_name = "#{root_namespace}::Image::#{variator_class_name}Variator"
68
+ say ""
69
+ say "Image variator #{full_class_name} created!", :green
70
+ say ""
71
+ say "Usage:"
72
+ say " # Generate variations of an image"
73
+ say " result = #{full_class_name}.call(image: 'logo.png', count: 4)"
74
+ say " result.urls # => ['https://...', ...]"
75
+ say ""
76
+ say " # Override settings at runtime"
77
+ say " result = #{full_class_name}.call("
78
+ say " image: 'logo.png',"
79
+ say " variation_strength: 0.7,"
80
+ say " count: 3"
81
+ say " )"
82
+ say ""
83
+ end
84
+
85
+ private
86
+
87
+ def root_directory
88
+ @root_directory ||= options[:root] || RubyLLM::Agents.configuration.root_directory
89
+ end
90
+
91
+ def root_namespace
92
+ @root_namespace ||= options[:namespace] || camelize(root_directory)
93
+ end
94
+
95
+ def camelize(str)
96
+ return "AI" if str.downcase == "ai"
97
+ return "ML" if str.downcase == "ml"
98
+ return "LLM" if str.downcase == "llm"
99
+ str.split(/[-_]/).map(&:capitalize).join
100
+ end
101
+ end
102
+ end
@@ -8,11 +8,13 @@ module RubyLlmAgents
8
8
  #
9
9
  # Usage:
10
10
  # rails generate ruby_llm_agents:install
11
+ # rails generate ruby_llm_agents:install --root=ai
11
12
  #
12
13
  # This will:
13
14
  # - Create the migration for ruby_llm_agents_executions table
14
15
  # - Create the initializer at config/initializers/ruby_llm_agents.rb
15
- # - Create app/agents/application_agent.rb base class
16
+ # - Create app/{root}/agents/application_agent.rb base class
17
+ # - Create app/{root}/text/embedders/application_embedder.rb base class
16
18
  # - Optionally mount the dashboard engine in routes
17
19
  #
18
20
  class InstallGenerator < ::Rails::Generators::Base
@@ -26,6 +28,14 @@ module RubyLlmAgents
26
28
  desc: "Skip generating the initializer file"
27
29
  class_option :mount_dashboard, type: :boolean, default: true,
28
30
  desc: "Mount the dashboard engine in routes"
31
+ class_option :root,
32
+ type: :string,
33
+ default: nil,
34
+ desc: "Root directory name (default: uses config or 'llm')"
35
+ class_option :namespace,
36
+ type: :string,
37
+ default: nil,
38
+ desc: "Root namespace (default: camelized root or config)"
29
39
 
30
40
  def create_migration_file
31
41
  return if options[:skip_migration]
@@ -42,12 +52,44 @@ module RubyLlmAgents
42
52
  template "initializer.rb.tt", "config/initializers/ruby_llm_agents.rb"
43
53
  end
44
54
 
45
- def create_agents_directory
46
- empty_directory "app/agents"
55
+ def create_directory_structure
56
+ say_status :create, "#{root_directory}/ directory structure", :green
57
+
58
+ # Create agents directory
59
+ empty_directory "app/#{root_directory}/agents"
60
+
61
+ # Create text/embedders directory
62
+ empty_directory "app/#{root_directory}/text/embedders"
47
63
  end
48
64
 
49
65
  def create_application_agent
50
- template "application_agent.rb.tt", "app/agents/application_agent.rb"
66
+ @root_namespace = root_namespace
67
+ template "application_agent.rb.tt", "app/#{root_directory}/agents/application_agent.rb"
68
+ end
69
+
70
+ def create_application_embedder
71
+ @root_namespace = root_namespace
72
+ @text_namespace = "#{root_namespace}::Text"
73
+ template "application_embedder.rb.tt", "app/#{root_directory}/text/embedders/application_embedder.rb"
74
+ end
75
+
76
+ def create_skill_files
77
+ @root_namespace = root_namespace
78
+ say_status :create, "skill documentation files", :green
79
+
80
+ # Create agents skill file
81
+ template "skills/AGENTS.md.tt", "app/#{root_directory}/agents/AGENTS.md"
82
+
83
+ # Create embedders skill file
84
+ template "skills/EMBEDDERS.md.tt", "app/#{root_directory}/text/embedders/EMBEDDERS.md"
85
+
86
+ # Create tools directory and skill file
87
+ empty_directory "app/#{root_directory}/tools"
88
+ template "skills/TOOLS.md.tt", "app/#{root_directory}/tools/TOOLS.md"
89
+
90
+ # Create workflows directory and skill file
91
+ empty_directory "app/#{root_directory}/workflows"
92
+ template "skills/WORKFLOWS.md.tt", "app/#{root_directory}/workflows/WORKFLOWS.md"
51
93
  end
52
94
 
53
95
  def mount_dashboard_engine
@@ -69,6 +111,24 @@ module RubyLlmAgents
69
111
  say ""
70
112
  say "RubyLLM::Agents has been installed!", :green
71
113
  say ""
114
+ say "Directory structure created:"
115
+ say " app/#{root_directory}/"
116
+ say " ├── agents/"
117
+ say " │ ├── application_agent.rb"
118
+ say " │ └── AGENTS.md"
119
+ say " ├── text/"
120
+ say " │ └── embedders/"
121
+ say " │ ├── application_embedder.rb"
122
+ say " │ └── EMBEDDERS.md"
123
+ say " ├── tools/"
124
+ say " │ └── TOOLS.md"
125
+ say " └── workflows/"
126
+ say " └── WORKFLOWS.md"
127
+ say ""
128
+ say "Skill files (*.md) help AI coding assistants understand how to use this gem."
129
+ say ""
130
+ say "Namespace: #{root_namespace}::"
131
+ say ""
72
132
  say "Next steps:"
73
133
  say " 1. Run migrations: rails db:migrate"
74
134
  say " 2. Generate an agent: rails generate ruby_llm_agents:agent MyAgent query:required"
@@ -85,5 +145,17 @@ module RubyLlmAgents
85
145
  def db_migrate_path
86
146
  "db/migrate"
87
147
  end
148
+
149
+ def root_directory
150
+ @root_directory ||= options[:root] || RubyLLM::Agents.configuration.root_directory
151
+ end
152
+
153
+ def root_namespace
154
+ @root_namespace ||= options[:namespace] || camelize(root_directory)
155
+ end
156
+
157
+ def camelize(str)
158
+ str.split(/[-_]/).map(&:capitalize).join
159
+ end
88
160
  end
89
161
  end