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,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration to create the api_configurations table
4
+ #
5
+ # This table stores API key configurations that can be managed via the dashboard.
6
+ # Supports both global settings and per-tenant overrides.
7
+ #
8
+ # Resolution priority: per-tenant DB > global DB > config file (RubyLLM.configure)
9
+ #
10
+ # Features:
11
+ # - Encrypted storage for all API keys (using Rails encrypted attributes)
12
+ # - Support for all major LLM providers
13
+ # - Custom endpoint configuration
14
+ # - Connection settings
15
+ # - Default model configuration
16
+ #
17
+ # Run with: rails db:migrate
18
+ class CreateRubyLLMAgentsApiConfigurations < ActiveRecord::Migration<%= migration_version %>
19
+ def change
20
+ create_table :ruby_llm_agents_api_configurations do |t|
21
+ # Scope type: 'global' or 'tenant'
22
+ t.string :scope_type, null: false, default: 'global'
23
+ # Tenant ID when scope_type='tenant'
24
+ t.string :scope_id
25
+
26
+ # === Encrypted API Keys ===
27
+ # Rails encrypts stores encrypted data in the same-named column
28
+ # Primary providers
29
+ t.text :openai_api_key
30
+ t.text :anthropic_api_key
31
+ t.text :gemini_api_key
32
+
33
+ # Additional providers
34
+ t.text :deepseek_api_key
35
+ t.text :mistral_api_key
36
+ t.text :perplexity_api_key
37
+ t.text :openrouter_api_key
38
+ t.text :gpustack_api_key
39
+ t.text :xai_api_key
40
+ t.text :ollama_api_key
41
+
42
+ # AWS Bedrock
43
+ t.text :bedrock_api_key
44
+ t.text :bedrock_secret_key
45
+ t.text :bedrock_session_token
46
+ t.string :bedrock_region
47
+
48
+ # Google Vertex AI
49
+ t.text :vertexai_credentials
50
+ t.string :vertexai_project_id
51
+ t.string :vertexai_location
52
+
53
+ # === Custom Endpoints ===
54
+ t.string :openai_api_base
55
+ t.string :gemini_api_base
56
+ t.string :ollama_api_base
57
+ t.string :gpustack_api_base
58
+ t.string :xai_api_base
59
+
60
+ # === OpenAI Options ===
61
+ t.string :openai_organization_id
62
+ t.string :openai_project_id
63
+
64
+ # === Default Models ===
65
+ t.string :default_model
66
+ t.string :default_embedding_model
67
+ t.string :default_image_model
68
+ t.string :default_moderation_model
69
+
70
+ # === Connection Settings ===
71
+ t.integer :request_timeout
72
+ t.integer :max_retries
73
+ t.decimal :retry_interval, precision: 5, scale: 2
74
+ t.decimal :retry_backoff_factor, precision: 5, scale: 2
75
+ t.decimal :retry_interval_randomness, precision: 5, scale: 2
76
+ t.string :http_proxy
77
+
78
+ # Whether to inherit from global config for unset values
79
+ t.boolean :inherit_global_defaults, default: true
80
+
81
+ t.timestamps
82
+ end
83
+
84
+ # Ensure unique scope_type + scope_id combinations
85
+ add_index :ruby_llm_agents_api_configurations, [:scope_type, :scope_id], unique: true, name: 'idx_api_configs_scope'
86
+
87
+ # Index for faster tenant lookups
88
+ add_index :ruby_llm_agents_api_configurations, :scope_id, name: 'idx_api_configs_scope_id'
89
+ end
90
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Text
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Embedder < ApplicationEmbedder
10
+ <%- else -%>
11
+ class <%= class_name %>Embedder < ApplicationEmbedder
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ <% if options[:dimensions] -%>
16
+ dimensions <%= options[:dimensions] %>
17
+ <% end -%>
18
+ <% if options[:batch_size] != 100 -%>
19
+ batch_size <%= options[:batch_size] %>
20
+ <% end -%>
21
+ <% if options[:cache] -%>
22
+
23
+ # Caching
24
+ cache_for <%= options[:cache] %>
25
+ <% end -%>
26
+
27
+ # Optional: Custom preprocessing
28
+ # Override this method to normalize/clean text before embedding
29
+ #
30
+ # def preprocess(text)
31
+ # text.strip.downcase.gsub(/\s+/, ' ')
32
+ # end
33
+ <%- if class_name.include?("::") -%>
34
+ <%- (class_name.split("::").length + 1).times do |i| -%>
35
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
36
+ <%- end -%>
37
+ <%- else -%>
38
+ end
39
+ <%- end -%>
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Analyzer < ApplicationImageAnalyzer
10
+ <%- else -%>
11
+ class <%= class_name %>Analyzer < ApplicationImageAnalyzer
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ analysis_type :<%= options[:analysis_type] %>
16
+ <% if options[:extract_colors] -%>
17
+ extract_colors true
18
+ <% end -%>
19
+ <% if options[:detect_objects] -%>
20
+ detect_objects true
21
+ <% end -%>
22
+ <% if options[:extract_text] -%>
23
+ extract_text true
24
+ <% end -%>
25
+ max_tags <%= options[:max_tags] %>
26
+ <% if options[:cache] -%>
27
+
28
+ # Caching
29
+ cache_for <%= options[:cache] %>
30
+ <% end -%>
31
+
32
+ # Optional: Custom analysis prompt
33
+ # prompt "Analyze this image and describe..."
34
+
35
+ # Optional: Description
36
+ # description "Analyzes <%= class_name.downcase %> images"
37
+ <%- if class_name.include?("::") -%>
38
+ <%- (class_name.split("::").length + 1).times do |i| -%>
39
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
40
+ <%- end -%>
41
+ <%- else -%>
42
+ end
43
+ <%- end -%>
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Editor < ApplicationImageEditor
10
+ <%- else -%>
11
+ class <%= class_name %>Editor < ApplicationImageEditor
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ size "<%= options[:size] %>"
16
+ <% if options[:content_policy] != "standard" -%>
17
+ content_policy :<%= options[:content_policy] %>
18
+ <% end -%>
19
+ <% if options[:cache] -%>
20
+
21
+ # Caching
22
+ cache_for <%= options[:cache] %>
23
+ <% end -%>
24
+
25
+ # Optional: Description
26
+ # description "Edits <%= class_name.downcase %> images"
27
+ <%- if class_name.include?("::") -%>
28
+ <%- (class_name.split("::").length + 1).times do |i| -%>
29
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
30
+ <%- end -%>
31
+ <%- else -%>
32
+ end
33
+ <%- end -%>
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Generator < ApplicationImageGenerator
10
+ <%- else -%>
11
+ class <%= class_name %>Generator < ApplicationImageGenerator
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ size "<%= options[:size] %>"
16
+ quality "<%= options[:quality] %>"
17
+ style "<%= options[:style] %>"
18
+ <% if options[:content_policy] != "standard" -%>
19
+ content_policy :<%= options[:content_policy] %>
20
+ <% end -%>
21
+ <% if options[:cache] -%>
22
+
23
+ # Caching
24
+ cache_for <%= options[:cache] %>
25
+ <% end -%>
26
+
27
+ # Optional: Add a prompt template
28
+ # template "Professional {prompt}, high quality, detailed"
29
+
30
+ # Optional: Add negative prompts (for models that support it)
31
+ # negative_prompt "blurry, low quality, distorted"
32
+
33
+ # Optional: Custom preprocessing
34
+ # Override this method to modify prompts before generation
35
+ #
36
+ # def preprocess_prompt(prompt)
37
+ # "#{prompt}, professional quality"
38
+ # end
39
+ <%- if class_name.include?("::") -%>
40
+ <%- (class_name.split("::").length + 1).times do |i| -%>
41
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
42
+ <%- end -%>
43
+ <%- else -%>
44
+ end
45
+ <%- end -%>
46
+ end
47
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Pipeline < ApplicationImagePipeline
10
+ <%- else -%>
11
+ class <%= class_name %>Pipeline < ApplicationImagePipeline
12
+ <%- end -%>
13
+ # Pipeline steps
14
+ <%- parsed_steps.each do |step| -%>
15
+ <%- case step -%>
16
+ <%- when :generate -%>
17
+ step :<%= step %>, generator: <%= class_name %>Generator
18
+ <%- when :upscale -%>
19
+ step :<%= step %>, upscaler: <%= class_name %>Upscaler
20
+ <%- when :transform -%>
21
+ step :<%= step %>, transformer: <%= class_name %>Transformer
22
+ <%- when :analyze -%>
23
+ step :<%= step %>, analyzer: <%= class_name %>Analyzer
24
+ <%- when :remove_background -%>
25
+ step :<%= step %>, remover: <%= class_name %>BackgroundRemover
26
+ <%- else -%>
27
+ # step :<%= step %>, ...: <%= class_name %><%= step.to_s.camelize %>
28
+ <%- end -%>
29
+ <%- end -%>
30
+ <%- if options[:stop_on_error] == false -%>
31
+
32
+ stop_on_error false
33
+ <%- end -%>
34
+ <% if options[:cache] -%>
35
+
36
+ # Caching
37
+ cache_for <%= options[:cache] %>
38
+ <% end -%>
39
+
40
+ description "<%= class_name %> image processing pipeline"
41
+ version "1.0"
42
+ <%- if class_name.include?("::") -%>
43
+ <%- (class_name.split("::").length + 1).times do |i| -%>
44
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
45
+ <%- end -%>
46
+ <%- else -%>
47
+ end
48
+ <%- end -%>
49
+ end
50
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Transformer < ApplicationImageTransformer
10
+ <%- else -%>
11
+ class <%= class_name %>Transformer < ApplicationImageTransformer
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ size "<%= options[:size] %>"
16
+ strength <%= options[:strength] %>
17
+ <% if options[:content_policy] != "standard" -%>
18
+ content_policy :<%= options[:content_policy] %>
19
+ <% end -%>
20
+ <% if options[:template] -%>
21
+
22
+ # Prompt template
23
+ template "<%= options[:template] %>"
24
+ <% end -%>
25
+ <% if options[:cache] -%>
26
+
27
+ # Caching
28
+ cache_for <%= options[:cache] %>
29
+ <% end -%>
30
+
31
+ # Optional: Negative prompts
32
+ # negative_prompt "blurry, low quality, distorted"
33
+
34
+ # Optional: Description
35
+ # description "Transforms images into <%= class_name.downcase %> style"
36
+ <%- if class_name.include?("::") -%>
37
+ <%- (class_name.split("::").length + 1).times do |i| -%>
38
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
39
+ <%- end -%>
40
+ <%- else -%>
41
+ end
42
+ <%- end -%>
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Upscaler < ApplicationImageUpscaler
10
+ <%- else -%>
11
+ class <%= class_name %>Upscaler < ApplicationImageUpscaler
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ scale <%= options[:scale] %>
16
+ <% if options[:face_enhance] -%>
17
+ face_enhance true
18
+ <% end -%>
19
+ <% if options[:cache] -%>
20
+
21
+ # Caching
22
+ cache_for <%= options[:cache] %>
23
+ <% end -%>
24
+
25
+ # Optional: Denoise strength (0.0-1.0)
26
+ # denoise_strength 0.5
27
+
28
+ # Optional: Description
29
+ # description "Upscales <%= class_name.downcase %> images"
30
+ <%- if class_name.include?("::") -%>
31
+ <%- (class_name.split("::").length + 1).times do |i| -%>
32
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
33
+ <%- end -%>
34
+ <%- else -%>
35
+ end
36
+ <%- end -%>
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= @root_namespace %>
4
+ module Image
5
+ <%- if class_name.include?("::") -%>
6
+ <%- class_name.split("::")[0..-2].each_with_index do |mod, i| -%>
7
+ <%= " " * (i + 2) %>module <%= mod %>
8
+ <%- end -%>
9
+ <%= " " * (class_name.split("::").length + 1) %>class <%= class_name.split("::").last %>Variator < ApplicationImageVariator
10
+ <%- else -%>
11
+ class <%= class_name %>Variator < ApplicationImageVariator
12
+ <%- end -%>
13
+ # Model configuration
14
+ model "<%= options[:model] %>"
15
+ size "<%= options[:size] %>"
16
+ variation_strength <%= options[:variation_strength] %>
17
+ <% if options[:cache] -%>
18
+
19
+ # Caching
20
+ cache_for <%= options[:cache] %>
21
+ <% end -%>
22
+
23
+ # Optional: Description
24
+ # description "Creates variations of <%= class_name.downcase %> images"
25
+ <%- if class_name.include?("::") -%>
26
+ <%- (class_name.split("::").length + 1).times do |i| -%>
27
+ <%= " " * (class_name.split("::").length + 1 - i) %>end
28
+ <%- end -%>
29
+ <%- else -%>
30
+ end
31
+ <%- end -%>
32
+ end
33
+ end
@@ -0,0 +1,228 @@
1
+ # <%= @root_namespace %> Agents
2
+
3
+ This directory contains LLM-powered agents for the application. All agents inherit from `ApplicationAgent`.
4
+
5
+ ## Creating a New Agent
6
+
7
+ Use the generator:
8
+ ```bash
9
+ rails generate ruby_llm_agents:agent AgentName param1:required param2:default_value
10
+ ```
11
+
12
+ Or create manually by extending `ApplicationAgent`:
13
+ ```ruby
14
+ module <%= @root_namespace %>
15
+ class MyAgent < ApplicationAgent
16
+ # Configuration
17
+ model "gpt-4o"
18
+ temperature 0.0
19
+ version "1.0"
20
+
21
+ # Parameters
22
+ param :query, required: true
23
+ param :limit, default: 10
24
+
25
+ private
26
+
27
+ def system_prompt
28
+ "You are a helpful assistant."
29
+ end
30
+
31
+ def user_prompt
32
+ query
33
+ end
34
+ end
35
+ end
36
+ ```
37
+
38
+ ## DSL Reference
39
+
40
+ ### Model Configuration
41
+
42
+ | Method | Description | Example |
43
+ |--------|-------------|---------|
44
+ | `model` | LLM model to use | `model "gpt-4o"` |
45
+ | `temperature` | Response randomness (0.0-2.0) | `temperature 0.7` |
46
+ | `version` | Cache invalidation version | `version "2.0"` |
47
+ | `timeout` | Request timeout in seconds | `timeout 30` |
48
+ | `description` | Human-readable description | `description "Searches documents"` |
49
+
50
+ ### Parameters
51
+
52
+ ```ruby
53
+ param :name # Optional parameter
54
+ param :query, required: true # Required parameter
55
+ param :limit, default: 10 # With default value
56
+ param :count, type: Integer # With type validation
57
+ ```
58
+
59
+ Access parameters as methods: `query`, `limit`, etc.
60
+
61
+ ### Caching
62
+
63
+ ```ruby
64
+ cache 1.hour # Enable with TTL
65
+ cache_for 30.minutes # Alias for cache
66
+
67
+ cache_key_includes :user_id, :query # Only these params in cache key
68
+ cache_key_excludes :timestamp # Exclude from cache key
69
+ ```
70
+
71
+ ### Reliability (Retries & Fallbacks)
72
+
73
+ ```ruby
74
+ # Individual settings
75
+ retries max: 3, backoff: :exponential, base: 0.4, max_delay: 3.0
76
+ fallback_models "gpt-4o-mini", "claude-3-haiku"
77
+ total_timeout 30
78
+ circuit_breaker errors: 5, within: 60, cooldown: 300
79
+
80
+ # Or grouped in a block
81
+ reliability do
82
+ retries max: 3, backoff: :exponential
83
+ fallback_models "gpt-4o-mini"
84
+ total_timeout 30
85
+ circuit_breaker errors: 5, within: 60
86
+ end
87
+ ```
88
+
89
+ ### Streaming
90
+
91
+ ```ruby
92
+ streaming true # Enable streaming by default
93
+ ```
94
+
95
+ ### Tools
96
+
97
+ ```ruby
98
+ tools [SearchTool, CalculatorTool] # Make tools available to agent
99
+ ```
100
+
101
+ ### Extended Thinking
102
+
103
+ ```ruby
104
+ thinking effort: :high # Enable extended thinking
105
+ thinking budget: 10000 # With token budget
106
+ ```
107
+
108
+ ### Moderation
109
+
110
+ ```ruby
111
+ moderation :input # Check input before LLM call
112
+ moderation :output # Check output after LLM call
113
+ moderation :both # Check both
114
+ ```
115
+
116
+ ## Required Methods
117
+
118
+ ### `user_prompt` (required)
119
+ The prompt sent to the LLM. Must return a String.
120
+
121
+ ### `system_prompt` (optional)
122
+ Instructions for the LLM. Return nil for no system prompt.
123
+
124
+ ## Optional Overrides
125
+
126
+ ### `schema`
127
+ Return a `RubyLLM::Schema` for structured JSON output:
128
+ ```ruby
129
+ def schema
130
+ @schema ||= RubyLLM::Schema.create do
131
+ string :result, description: "The result"
132
+ integer :confidence, description: "Confidence 1-100"
133
+ array :tags do
134
+ string
135
+ end
136
+ end
137
+ end
138
+ ```
139
+
140
+ ### `process_response(response)`
141
+ Transform the LLM response before returning:
142
+ ```ruby
143
+ def process_response(response)
144
+ content = response.content
145
+ # Custom processing
146
+ content
147
+ end
148
+ ```
149
+
150
+ ### `messages`
151
+ Provide conversation history for multi-turn:
152
+ ```ruby
153
+ def messages
154
+ [
155
+ { role: :user, content: "Previous question" },
156
+ { role: :assistant, content: "Previous answer" }
157
+ ]
158
+ end
159
+ ```
160
+
161
+ ### `cache_key_data`
162
+ Customize what goes into the cache key:
163
+ ```ruby
164
+ def cache_key_data
165
+ { query: query, locale: I18n.locale }
166
+ end
167
+ ```
168
+
169
+ ### `execution_metadata`
170
+ Add custom data to execution logs:
171
+ ```ruby
172
+ def execution_metadata
173
+ { request_id: params[:request_id] }
174
+ end
175
+ ```
176
+
177
+ ## Calling Agents
178
+
179
+ ```ruby
180
+ # Basic call
181
+ result = <%= @root_namespace %>::MyAgent.call(query: "hello")
182
+
183
+ # Access result
184
+ result.content # The response content
185
+ result.input_tokens # Tokens used in prompt
186
+ result.output_tokens # Tokens in response
187
+ result.total_cost # Cost in USD
188
+
189
+ # Debug mode (no API call)
190
+ result = <%= @root_namespace %>::MyAgent.call(query: "hello", dry_run: true)
191
+
192
+ # Skip cache
193
+ result = <%= @root_namespace %>::MyAgent.call(query: "hello", skip_cache: true)
194
+
195
+ # With attachments
196
+ result = <%= @root_namespace %>::MyAgent.call(query: "describe this", with: "image.png")
197
+
198
+ # Streaming
199
+ <%= @root_namespace %>::MyAgent.stream(query: "hello") do |chunk|
200
+ print chunk.content
201
+ end
202
+
203
+ # Multi-tenancy
204
+ result = <%= @root_namespace %>::MyAgent.call(query: "hello", tenant: current_user)
205
+ ```
206
+
207
+ ## Testing Agents
208
+
209
+ ```ruby
210
+ RSpec.describe <%= @root_namespace %>::MyAgent do
211
+ describe ".call" do
212
+ it "returns expected result" do
213
+ # Use dry_run for unit tests
214
+ result = described_class.call(query: "test", dry_run: true)
215
+ expect(result.content[:user_prompt]).to eq("test")
216
+ end
217
+ end
218
+ end
219
+ ```
220
+
221
+ ## Best Practices
222
+
223
+ 1. **Keep prompts focused** - One agent, one task
224
+ 2. **Use structured output** - Define schemas for predictable responses
225
+ 3. **Enable caching** - For deterministic queries
226
+ 4. **Set appropriate temperatures** - 0.0 for deterministic, higher for creative
227
+ 5. **Configure retries** - For production reliability
228
+ 6. **Version your agents** - Bump version when changing prompts