ruby_llm-agents 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/ruby_llm/agents/paginatable.rb +9 -3
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +58 -0
- data/app/controllers/ruby_llm/agents/agents_controller.rb +59 -16
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +144 -20
- data/app/controllers/ruby_llm/agents/executions_controller.rb +13 -16
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +279 -90
- data/app/helpers/ruby_llm/agents/application_helper.rb +100 -0
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +84 -0
- data/app/mailers/ruby_llm/agents/application_mailer.rb +28 -0
- data/app/models/ruby_llm/agents/execution/analytics.rb +170 -20
- data/app/models/ruby_llm/agents/execution/scopes.rb +0 -31
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -129
- data/app/models/ruby_llm/agents/execution.rb +50 -14
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +277 -0
- data/app/models/ruby_llm/agents/tenant/configurable.rb +135 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +310 -0
- data/app/models/ruby_llm/agents/tenant.rb +146 -0
- data/app/models/ruby_llm/agents/tenant_budget.rb +12 -253
- data/app/services/ruby_llm/agents/agent_registry.rb +18 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +72 -76
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -12
- data/app/views/ruby_llm/agents/agents/_sortable_header.html.erb +56 -0
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +5 -15
- data/app/views/ruby_llm/agents/agents/index.html.erb +271 -100
- data/app/views/ruby_llm/agents/agents/show.html.erb +1 -0
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +107 -0
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +18 -0
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +4 -1
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +66 -359
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +56 -0
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +115 -0
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +35 -60
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +17 -6
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +373 -72
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +0 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +51 -39
- data/app/views/ruby_llm/agents/executions/_list.html.erb +53 -195
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +5 -20
- data/app/views/ruby_llm/agents/executions/index.html.erb +7 -83
- data/app/views/ruby_llm/agents/executions/show.html.erb +10 -20
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +2 -1
- data/app/views/ruby_llm/agents/shared/_doc_link.html.erb +12 -0
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +3 -15
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_sortable_header.html.erb +53 -0
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +7 -0
- data/app/views/ruby_llm/agents/shared/_status_dot.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +9 -35
- data/app/views/ruby_llm/agents/system_config/show.html.erb +4 -1
- data/app/views/ruby_llm/agents/tenants/index.html.erb +4 -1
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +7 -15
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +539 -0
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +920 -0
- data/app/views/ruby_llm/agents/workflows/index.html.erb +179 -0
- data/app/views/ruby_llm/agents/workflows/show.html.erb +164 -139
- data/config/routes.rb +1 -1
- data/lib/generators/ruby_llm_agents/agent_generator.rb +6 -36
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +5 -38
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +8 -41
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +18 -46
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/install_generator.rb +33 -56
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +480 -0
- data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +42 -22
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +2 -2
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +8 -39
- data/lib/generators/ruby_llm_agents/templates/add_tenant_to_executions_migration.rb.tt +13 -2
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +5 -8
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +40 -42
- data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +24 -26
- data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +31 -33
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +125 -127
- data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +20 -18
- data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +38 -40
- data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +42 -44
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +48 -0
- data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/create_tenant_budgets_migration.rb.tt +11 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +72 -0
- data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +15 -17
- data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +25 -27
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +17 -19
- data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +15 -17
- data/lib/generators/ruby_llm_agents/templates/rename_tenant_budgets_to_tenants_migration.rb.tt +34 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +87 -24
- data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +21 -27
- data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +46 -54
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +31 -39
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +22 -28
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +53 -63
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +46 -56
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +23 -31
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +22 -30
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +23 -31
- data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +38 -46
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +7 -7
- data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +59 -71
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +274 -23
- data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +29 -31
- data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +28 -30
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +10 -43
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +26 -0
- data/lib/ruby_llm/agents/core/configuration.rb +55 -43
- data/lib/ruby_llm/agents/core/llm_tenant.rb +60 -60
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +26 -0
- data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +4 -2
- data/lib/ruby_llm/agents/pipeline.rb +69 -0
- data/lib/ruby_llm/agents/workflow/approval.rb +205 -0
- data/lib/ruby_llm/agents/workflow/approval_store.rb +179 -0
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +467 -0
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +244 -0
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +289 -0
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +107 -0
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +150 -0
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +187 -0
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +352 -0
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +415 -0
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +257 -0
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +317 -0
- data/lib/ruby_llm/agents/workflow/dsl.rb +576 -0
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +2 -7
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +117 -0
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +117 -0
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +180 -0
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +121 -0
- data/lib/ruby_llm/agents/workflow/notifiers.rb +70 -0
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +190 -23
- data/lib/ruby_llm/agents/workflow/result.rb +202 -0
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +206 -0
- data/lib/ruby_llm/agents/workflow/wait_result.rb +213 -0
- metadata +43 -6
- data/app/views/ruby_llm/agents/dashboard/_execution_item.html.erb +0 -66
- data/lib/ruby_llm/agents/workflow/parallel.rb +0 -299
- data/lib/ruby_llm/agents/workflow/pipeline.rb +0 -306
- data/lib/ruby_llm/agents/workflow/router.rb +0 -429
data/lib/generators/ruby_llm_agents/templates/rename_tenant_budgets_to_tenants_migration.rb.tt
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration to rename tenant_budgets table to tenants
|
|
4
|
+
#
|
|
5
|
+
# This migration is for users upgrading from an older version of the gem
|
|
6
|
+
# where the table was called 'ruby_llm_agents_tenant_budgets'.
|
|
7
|
+
#
|
|
8
|
+
# If you're doing a fresh install, use the create_tenants_migration instead.
|
|
9
|
+
#
|
|
10
|
+
# Run with: rails db:migrate
|
|
11
|
+
class RenameTenantBudgetsToTenants < ActiveRecord::Migration<%= migration_version %>
|
|
12
|
+
def change
|
|
13
|
+
# Rename the table
|
|
14
|
+
rename_table :ruby_llm_agents_tenant_budgets, :ruby_llm_agents_tenants
|
|
15
|
+
|
|
16
|
+
# Add new columns for Tenant model
|
|
17
|
+
add_column :ruby_llm_agents_tenants, :active, :boolean, default: true
|
|
18
|
+
add_column :ruby_llm_agents_tenants, :metadata, :json, null: false, default: {}
|
|
19
|
+
|
|
20
|
+
# Add index for active status
|
|
21
|
+
add_index :ruby_llm_agents_tenants, :active
|
|
22
|
+
|
|
23
|
+
# Rename indexes to match new table name
|
|
24
|
+
# Note: Some databases handle this automatically when renaming tables
|
|
25
|
+
# If you get an error about missing indexes, you may need to adjust this
|
|
26
|
+
|
|
27
|
+
# The polymorphic index needs to be renamed if it exists
|
|
28
|
+
if index_exists?(:ruby_llm_agents_tenants, [:tenant_record_type, :tenant_record_id], name: "index_tenant_budgets_on_tenant_record")
|
|
29
|
+
rename_index :ruby_llm_agents_tenants,
|
|
30
|
+
"index_tenant_budgets_on_tenant_record",
|
|
31
|
+
"index_tenants_on_tenant_record"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agents
|
|
2
2
|
|
|
3
3
|
This directory contains LLM-powered agents for the application. All agents inherit from `ApplicationAgent`.
|
|
4
4
|
|
|
@@ -11,30 +11,47 @@ rails generate ruby_llm_agents:agent AgentName param1:required param2:default_va
|
|
|
11
11
|
|
|
12
12
|
Or create manually by extending `ApplicationAgent`:
|
|
13
13
|
```ruby
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
version "1.0"
|
|
14
|
+
class MyAgent < ApplicationAgent
|
|
15
|
+
# Configuration
|
|
16
|
+
model "gpt-4o"
|
|
17
|
+
temperature 0.0
|
|
18
|
+
version "1.0"
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
# Parameters
|
|
21
|
+
param :query, required: true
|
|
22
|
+
param :limit, default: 10
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
private
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
def system_prompt
|
|
27
|
+
"You are a helpful assistant."
|
|
28
|
+
end
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
30
|
+
def user_prompt
|
|
31
|
+
query
|
|
34
32
|
end
|
|
35
33
|
end
|
|
36
34
|
```
|
|
37
35
|
|
|
36
|
+
## Directory Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
app/agents/
|
|
40
|
+
├── application_agent.rb # Base class for all agents
|
|
41
|
+
├── concerns/ # Shared agent mixins
|
|
42
|
+
├── my_agent.rb # Task agents at root
|
|
43
|
+
├── images/ # Image operation agents
|
|
44
|
+
│ ├── product_generator.rb
|
|
45
|
+
│ └── content_analyzer.rb
|
|
46
|
+
├── audio/ # Audio operation agents
|
|
47
|
+
│ ├── meeting_transcriber.rb
|
|
48
|
+
│ └── voice_narrator.rb
|
|
49
|
+
├── embedders/ # Embedding agents
|
|
50
|
+
│ └── semantic_embedder.rb
|
|
51
|
+
└── moderators/ # Moderation agents
|
|
52
|
+
└── content_moderator.rb
|
|
53
|
+
```
|
|
54
|
+
|
|
38
55
|
## DSL Reference
|
|
39
56
|
|
|
40
57
|
### Model Configuration
|
|
@@ -178,7 +195,7 @@ end
|
|
|
178
195
|
|
|
179
196
|
```ruby
|
|
180
197
|
# Basic call
|
|
181
|
-
result =
|
|
198
|
+
result = MyAgent.call(query: "hello")
|
|
182
199
|
|
|
183
200
|
# Access result
|
|
184
201
|
result.content # The response content
|
|
@@ -187,27 +204,73 @@ result.output_tokens # Tokens in response
|
|
|
187
204
|
result.total_cost # Cost in USD
|
|
188
205
|
|
|
189
206
|
# Debug mode (no API call)
|
|
190
|
-
result =
|
|
207
|
+
result = MyAgent.call(query: "hello", dry_run: true)
|
|
191
208
|
|
|
192
209
|
# Skip cache
|
|
193
|
-
result =
|
|
210
|
+
result = MyAgent.call(query: "hello", skip_cache: true)
|
|
194
211
|
|
|
195
212
|
# With attachments
|
|
196
|
-
result =
|
|
213
|
+
result = MyAgent.call(query: "describe this", with: "image.png")
|
|
197
214
|
|
|
198
215
|
# Streaming
|
|
199
|
-
|
|
216
|
+
MyAgent.stream(query: "hello") do |chunk|
|
|
200
217
|
print chunk.content
|
|
201
218
|
end
|
|
202
219
|
|
|
203
220
|
# Multi-tenancy
|
|
204
|
-
result =
|
|
221
|
+
result = MyAgent.call(query: "hello", tenant: current_user)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Specialized Agents
|
|
225
|
+
|
|
226
|
+
### Image Agents
|
|
227
|
+
```ruby
|
|
228
|
+
# app/agents/images/product_generator.rb
|
|
229
|
+
module Images
|
|
230
|
+
class ProductGenerator < RubyLLM::Agents::Image::Generator
|
|
231
|
+
model "dall-e-3"
|
|
232
|
+
size "1024x1024"
|
|
233
|
+
quality "hd"
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Usage
|
|
238
|
+
result = Images::ProductGenerator.call(prompt: "A product photo")
|
|
239
|
+
result.url # Image URL
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Audio Agents
|
|
243
|
+
```ruby
|
|
244
|
+
# app/agents/audio/meeting_transcriber.rb
|
|
245
|
+
module Audio
|
|
246
|
+
class MeetingTranscriber < RubyLLM::Agents::Audio::Transcriber
|
|
247
|
+
model "whisper-1"
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Usage
|
|
252
|
+
result = Audio::MeetingTranscriber.call(audio: "meeting.mp3")
|
|
253
|
+
result.text # Transcribed text
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Embedding Agents
|
|
257
|
+
```ruby
|
|
258
|
+
# app/agents/embedders/semantic_embedder.rb
|
|
259
|
+
module Embedders
|
|
260
|
+
class SemanticEmbedder < RubyLLM::Agents::Embedder
|
|
261
|
+
model "text-embedding-3-large"
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Usage
|
|
266
|
+
result = Embedders::SemanticEmbedder.call(text: "Hello world")
|
|
267
|
+
result.embedding # Vector array
|
|
205
268
|
```
|
|
206
269
|
|
|
207
270
|
## Testing Agents
|
|
208
271
|
|
|
209
272
|
```ruby
|
|
210
|
-
RSpec.describe
|
|
273
|
+
RSpec.describe MyAgent do
|
|
211
274
|
describe ".call" do
|
|
212
275
|
it "returns expected result" do
|
|
213
276
|
# Use dry_run for unit tests
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Images Background Removers
|
|
2
2
|
|
|
3
3
|
This directory contains background removal services. All removers inherit from `ApplicationBackgroundRemover`.
|
|
4
4
|
|
|
@@ -12,13 +12,11 @@ rails generate ruby_llm_agents:background_remover Product --edge_refinement high
|
|
|
12
12
|
|
|
13
13
|
Or create manually:
|
|
14
14
|
```ruby
|
|
15
|
-
module
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
output_format :png
|
|
21
|
-
end
|
|
15
|
+
module Images
|
|
16
|
+
class ProductRemover < ApplicationBackgroundRemover
|
|
17
|
+
model "remove-bg"
|
|
18
|
+
edge_refinement :high
|
|
19
|
+
output_format :png
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
```
|
|
@@ -45,7 +43,7 @@ end
|
|
|
45
43
|
## Using Removers
|
|
46
44
|
|
|
47
45
|
```ruby
|
|
48
|
-
result =
|
|
46
|
+
result = Images::ProductRemover.call(image: "product.jpg")
|
|
49
47
|
|
|
50
48
|
result.url # Image URL with transparent background
|
|
51
49
|
result.image_data # PNG binary data
|
|
@@ -56,7 +54,7 @@ result.save("product_nobg.png")
|
|
|
56
54
|
### With Background Color
|
|
57
55
|
|
|
58
56
|
```ruby
|
|
59
|
-
result =
|
|
57
|
+
result = Images::ProductRemover.call(
|
|
60
58
|
image: "product.jpg",
|
|
61
59
|
background_color: "#FFFFFF" # White background
|
|
62
60
|
)
|
|
@@ -65,7 +63,7 @@ result = <%= @root_namespace %>::Image::ProductRemover.call(
|
|
|
65
63
|
### Override Settings
|
|
66
64
|
|
|
67
65
|
```ruby
|
|
68
|
-
result =
|
|
66
|
+
result = Images::ProductRemover.call(
|
|
69
67
|
image: "complex_photo.jpg",
|
|
70
68
|
edge_refinement: :high,
|
|
71
69
|
output_format: :webp
|
|
@@ -77,14 +75,12 @@ result = <%= @root_namespace %>::Image::ProductRemover.call(
|
|
|
77
75
|
### E-commerce Product Photos
|
|
78
76
|
|
|
79
77
|
```ruby
|
|
80
|
-
module
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
cache_for 30.days # Product images don't change often
|
|
87
|
-
end
|
|
78
|
+
module Images
|
|
79
|
+
class ProductBackgroundRemover < ApplicationBackgroundRemover
|
|
80
|
+
edge_refinement :high
|
|
81
|
+
output_format :png
|
|
82
|
+
|
|
83
|
+
cache_for 30.days # Product images don't change often
|
|
88
84
|
end
|
|
89
85
|
end
|
|
90
86
|
```
|
|
@@ -92,24 +88,22 @@ end
|
|
|
92
88
|
### Profile Photo Processing
|
|
93
89
|
|
|
94
90
|
```ruby
|
|
95
|
-
module
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
output_format :png
|
|
100
|
-
end
|
|
91
|
+
module Images
|
|
92
|
+
class AvatarBackgroundRemover < ApplicationBackgroundRemover
|
|
93
|
+
edge_refinement :high
|
|
94
|
+
output_format :png
|
|
101
95
|
end
|
|
102
96
|
end
|
|
103
97
|
|
|
104
98
|
# Remove background and resize
|
|
105
|
-
result =
|
|
99
|
+
result = Images::AvatarBackgroundRemover.call(image: uploaded_photo)
|
|
106
100
|
```
|
|
107
101
|
|
|
108
102
|
### Batch Processing
|
|
109
103
|
|
|
110
104
|
```ruby
|
|
111
105
|
Product.where(background_removed: false).find_each do |product|
|
|
112
|
-
result =
|
|
106
|
+
result = Images::ProductBackgroundRemover.call(
|
|
113
107
|
image: product.original_image.download
|
|
114
108
|
)
|
|
115
109
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Embedders
|
|
2
2
|
|
|
3
3
|
This directory contains text embedding services for vector search and semantic similarity. All embedders inherit from `ApplicationEmbedder`.
|
|
4
4
|
|
|
@@ -13,14 +13,12 @@ rails generate ruby_llm_agents:embedder Search --dimensions 512 --batch_size 50
|
|
|
13
13
|
|
|
14
14
|
Or create manually:
|
|
15
15
|
```ruby
|
|
16
|
-
module
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
cache_for 1.week
|
|
23
|
-
end
|
|
16
|
+
module Embedders
|
|
17
|
+
class DocumentEmbedder < ApplicationEmbedder
|
|
18
|
+
model "text-embedding-3-small"
|
|
19
|
+
dimensions 1536
|
|
20
|
+
batch_size 100
|
|
21
|
+
cache_for 1.week
|
|
24
22
|
end
|
|
25
23
|
end
|
|
26
24
|
```
|
|
@@ -76,7 +74,7 @@ end
|
|
|
76
74
|
### Single Text Embedding
|
|
77
75
|
|
|
78
76
|
```ruby
|
|
79
|
-
result =
|
|
77
|
+
result = Embedders::DocumentEmbedder.call(text: "Hello world")
|
|
80
78
|
|
|
81
79
|
result.embedding # [0.123, -0.456, ...] vector array
|
|
82
80
|
result.dimensions # 1536
|
|
@@ -90,7 +88,7 @@ Process multiple texts efficiently:
|
|
|
90
88
|
|
|
91
89
|
```ruby
|
|
92
90
|
texts = ["Hello world", "Goodbye world", "Ruby programming"]
|
|
93
|
-
result =
|
|
91
|
+
result = Embedders::DocumentEmbedder.call(texts: texts)
|
|
94
92
|
|
|
95
93
|
result.embeddings # Array of embedding vectors
|
|
96
94
|
result.count # 3
|
|
@@ -112,7 +110,7 @@ end
|
|
|
112
110
|
|
|
113
111
|
# Generate and store embedding
|
|
114
112
|
document = Document.find(1)
|
|
115
|
-
result =
|
|
113
|
+
result = Embedders::DocumentEmbedder.call(text: document.content)
|
|
116
114
|
document.update!(embedding: result.embedding)
|
|
117
115
|
```
|
|
118
116
|
|
|
@@ -120,7 +118,7 @@ document.update!(embedding: result.embedding)
|
|
|
120
118
|
|
|
121
119
|
```ruby
|
|
122
120
|
# Embed the query
|
|
123
|
-
query_result =
|
|
121
|
+
query_result = Embedders::SearchEmbedder.call(text: "ruby web framework")
|
|
124
122
|
|
|
125
123
|
# Find similar documents
|
|
126
124
|
similar = Document.nearest_neighbors(:embedding, query_result.embedding, distance: "cosine")
|
|
@@ -132,20 +130,18 @@ similar = Document.nearest_neighbors(:embedding, query_result.embedding, distanc
|
|
|
132
130
|
### Document Embedding
|
|
133
131
|
|
|
134
132
|
```ruby
|
|
135
|
-
module
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.truncate(8000)
|
|
148
|
-
end
|
|
133
|
+
module Embedders
|
|
134
|
+
class DocumentEmbedder < ApplicationEmbedder
|
|
135
|
+
model "text-embedding-3-large"
|
|
136
|
+
dimensions 1024
|
|
137
|
+
batch_size 50
|
|
138
|
+
cache_for 30.days
|
|
139
|
+
|
|
140
|
+
def preprocess(text)
|
|
141
|
+
text
|
|
142
|
+
.strip
|
|
143
|
+
.gsub(/\s+/, ' ')
|
|
144
|
+
.truncate(8000)
|
|
149
145
|
end
|
|
150
146
|
end
|
|
151
147
|
end
|
|
@@ -154,16 +150,14 @@ end
|
|
|
154
150
|
### Search Query Embedding
|
|
155
151
|
|
|
156
152
|
```ruby
|
|
157
|
-
module
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
text.strip.downcase
|
|
166
|
-
end
|
|
153
|
+
module Embedders
|
|
154
|
+
class SearchEmbedder < ApplicationEmbedder
|
|
155
|
+
model "text-embedding-3-small"
|
|
156
|
+
dimensions 512
|
|
157
|
+
cache_for 1.hour # Queries may repeat
|
|
158
|
+
|
|
159
|
+
def preprocess(text)
|
|
160
|
+
text.strip.downcase
|
|
167
161
|
end
|
|
168
162
|
end
|
|
169
163
|
end
|
|
@@ -172,20 +166,18 @@ end
|
|
|
172
166
|
### Code Embedding
|
|
173
167
|
|
|
174
168
|
```ruby
|
|
175
|
-
module
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
.strip
|
|
188
|
-
end
|
|
169
|
+
module Embedders
|
|
170
|
+
class CodeEmbedder < ApplicationEmbedder
|
|
171
|
+
model "text-embedding-3-large"
|
|
172
|
+
cache_for 7.days
|
|
173
|
+
|
|
174
|
+
def preprocess(text)
|
|
175
|
+
# Remove comments, normalize whitespace
|
|
176
|
+
text
|
|
177
|
+
.gsub(/#.*$/, '')
|
|
178
|
+
.gsub(/\/\/.*$/, '')
|
|
179
|
+
.gsub(/\s+/, ' ')
|
|
180
|
+
.strip
|
|
189
181
|
end
|
|
190
182
|
end
|
|
191
183
|
end
|
|
@@ -198,13 +190,13 @@ end
|
|
|
198
190
|
Document.find_each(batch_size: 100) do |doc|
|
|
199
191
|
next if doc.embedding.present?
|
|
200
192
|
|
|
201
|
-
result =
|
|
193
|
+
result = Embedders::DocumentEmbedder.call(text: doc.content)
|
|
202
194
|
doc.update!(embedding: result.embedding)
|
|
203
195
|
end
|
|
204
196
|
|
|
205
197
|
# Or batch multiple at once
|
|
206
198
|
texts = Document.where(embedding: nil).limit(100).pluck(:id, :content)
|
|
207
|
-
results =
|
|
199
|
+
results = Embedders::DocumentEmbedder.call(texts: texts.map(&:last))
|
|
208
200
|
|
|
209
201
|
texts.each_with_index do |(id, _), index|
|
|
210
202
|
Document.find(id).update!(embedding: results.embeddings[index])
|
|
@@ -214,7 +206,7 @@ end
|
|
|
214
206
|
## Testing Embedders
|
|
215
207
|
|
|
216
208
|
```ruby
|
|
217
|
-
RSpec.describe
|
|
209
|
+
RSpec.describe Embedders::DocumentEmbedder do
|
|
218
210
|
describe ".call" do
|
|
219
211
|
it "returns embedding vector" do
|
|
220
212
|
result = described_class.call(text: "Hello world")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Images Analyzers
|
|
2
2
|
|
|
3
3
|
This directory contains image analysis services. All analyzers inherit from `ApplicationImageAnalyzer`.
|
|
4
4
|
|
|
@@ -12,14 +12,12 @@ rails generate ruby_llm_agents:image_analyzer Product --model gpt-4o
|
|
|
12
12
|
|
|
13
13
|
Or create manually:
|
|
14
14
|
```ruby
|
|
15
|
-
module
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
detect_objects true
|
|
22
|
-
end
|
|
15
|
+
module Images
|
|
16
|
+
class ProductAnalyzer < ApplicationImageAnalyzer
|
|
17
|
+
model "gpt-4o"
|
|
18
|
+
analysis_type :detailed
|
|
19
|
+
extract_colors true
|
|
20
|
+
detect_objects true
|
|
23
21
|
end
|
|
24
22
|
end
|
|
25
23
|
```
|
|
@@ -41,7 +39,7 @@ end
|
|
|
41
39
|
## Using Analyzers
|
|
42
40
|
|
|
43
41
|
```ruby
|
|
44
|
-
result =
|
|
42
|
+
result = Images::ProductAnalyzer.call(image: "product.jpg")
|
|
45
43
|
|
|
46
44
|
result.description # Text description
|
|
47
45
|
result.objects # Detected objects
|
|
@@ -53,7 +51,7 @@ result.total_cost # Cost in USD
|
|
|
53
51
|
### Analyze URL
|
|
54
52
|
|
|
55
53
|
```ruby
|
|
56
|
-
result =
|
|
54
|
+
result = Images::ProductAnalyzer.call(
|
|
57
55
|
image: "https://example.com/photo.jpg"
|
|
58
56
|
)
|
|
59
57
|
```
|
|
@@ -61,16 +59,14 @@ result = <%= @root_namespace %>::Image::ProductAnalyzer.call(
|
|
|
61
59
|
### Custom Analysis Prompt
|
|
62
60
|
|
|
63
61
|
```ruby
|
|
64
|
-
module
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"Return JSON with scores from 1-10 for each aspect."
|
|
73
|
-
end
|
|
62
|
+
module Images
|
|
63
|
+
class CustomAnalyzer < ApplicationImageAnalyzer
|
|
64
|
+
model "gpt-4o"
|
|
65
|
+
analysis_type :custom
|
|
66
|
+
|
|
67
|
+
def analysis_prompt
|
|
68
|
+
"Analyze this image for: composition, lighting, color harmony. " \
|
|
69
|
+
"Return JSON with scores from 1-10 for each aspect."
|
|
74
70
|
end
|
|
75
71
|
end
|
|
76
72
|
end
|
|
@@ -81,15 +77,13 @@ end
|
|
|
81
77
|
### Content Moderation
|
|
82
78
|
|
|
83
79
|
```ruby
|
|
84
|
-
module
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"Return JSON with: safe (boolean), reason (string if unsafe)"
|
|
92
|
-
end
|
|
80
|
+
module Images
|
|
81
|
+
class ModerationAnalyzer < ApplicationImageAnalyzer
|
|
82
|
+
model "gpt-4o"
|
|
83
|
+
|
|
84
|
+
def analysis_prompt
|
|
85
|
+
"Analyze this image for inappropriate content. " \
|
|
86
|
+
"Return JSON with: safe (boolean), reason (string if unsafe)"
|
|
93
87
|
end
|
|
94
88
|
end
|
|
95
89
|
end
|
|
@@ -98,15 +92,13 @@ end
|
|
|
98
92
|
### Product Categorization
|
|
99
93
|
|
|
100
94
|
```ruby
|
|
101
|
-
module
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"Identify the product category, brand if visible, and condition."
|
|
109
|
-
end
|
|
95
|
+
module Images
|
|
96
|
+
class CategoryAnalyzer < ApplicationImageAnalyzer
|
|
97
|
+
model "gpt-4o"
|
|
98
|
+
detect_objects true
|
|
99
|
+
|
|
100
|
+
def analysis_prompt
|
|
101
|
+
"Identify the product category, brand if visible, and condition."
|
|
110
102
|
end
|
|
111
103
|
end
|
|
112
104
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Images Editors
|
|
2
2
|
|
|
3
3
|
This directory contains image editing services (inpainting, masked edits). All editors inherit from `ApplicationImageEditor`.
|
|
4
4
|
|
|
@@ -12,13 +12,11 @@ rails generate ruby_llm_agents:image_editor Background --model gpt-image-1
|
|
|
12
12
|
|
|
13
13
|
Or create manually:
|
|
14
14
|
```ruby
|
|
15
|
-
module
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
content_policy :standard
|
|
21
|
-
end
|
|
15
|
+
module Images
|
|
16
|
+
class BackgroundEditor < ApplicationImageEditor
|
|
17
|
+
model "gpt-image-1"
|
|
18
|
+
size "1024x1024"
|
|
19
|
+
content_policy :standard
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
```
|
|
@@ -36,7 +34,7 @@ end
|
|
|
36
34
|
### Basic Edit with Mask
|
|
37
35
|
|
|
38
36
|
```ruby
|
|
39
|
-
result =
|
|
37
|
+
result = Images::BackgroundEditor.call(
|
|
40
38
|
image: "photo.png",
|
|
41
39
|
mask: "mask.png",
|
|
42
40
|
prompt: "Beautiful sunset sky background"
|
|
@@ -51,7 +49,7 @@ result.save("edited.png")
|
|
|
51
49
|
|
|
52
50
|
```ruby
|
|
53
51
|
# Mask should be PNG with transparent areas indicating regions to edit
|
|
54
|
-
result =
|
|
52
|
+
result = Images::BackgroundEditor.call(
|
|
55
53
|
image: original_image,
|
|
56
54
|
mask: mask_with_transparent_areas,
|
|
57
55
|
prompt: "Modern city skyline"
|
|
@@ -63,15 +61,13 @@ result = <%= @root_namespace %>::Image::BackgroundEditor.call(
|
|
|
63
61
|
### Background Replacement
|
|
64
62
|
|
|
65
63
|
```ruby
|
|
66
|
-
module
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"Professional studio background, soft lighting"
|
|
74
|
-
end
|
|
64
|
+
module Images
|
|
65
|
+
class BackgroundReplacer < ApplicationImageEditor
|
|
66
|
+
model "gpt-image-1"
|
|
67
|
+
size "1024x1024"
|
|
68
|
+
|
|
69
|
+
def default_prompt
|
|
70
|
+
"Professional studio background, soft lighting"
|
|
75
71
|
end
|
|
76
72
|
end
|
|
77
73
|
end
|
|
@@ -80,15 +76,13 @@ end
|
|
|
80
76
|
### Object Removal
|
|
81
77
|
|
|
82
78
|
```ruby
|
|
83
|
-
module
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"Seamless continuation of the surrounding area"
|
|
91
|
-
end
|
|
79
|
+
module Images
|
|
80
|
+
class ObjectRemover < ApplicationImageEditor
|
|
81
|
+
model "gpt-image-1"
|
|
82
|
+
|
|
83
|
+
# Mask covers the object to remove
|
|
84
|
+
def default_prompt
|
|
85
|
+
"Seamless continuation of the surrounding area"
|
|
92
86
|
end
|
|
93
87
|
end
|
|
94
88
|
end
|