ruby_llm-agents 0.5.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.
- checksums.yaml +4 -4
- data/README.md +189 -31
- data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +355 -0
- data/app/helpers/ruby_llm/agents/application_helper.rb +25 -0
- data/app/models/ruby_llm/agents/execution.rb +3 -0
- data/app/models/ruby_llm/agents/tenant_budget.rb +58 -15
- data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +2 -29
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +13 -1
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +235 -0
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +70 -0
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +152 -0
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +63 -0
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +108 -0
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +91 -0
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +1 -1
- data/app/views/ruby_llm/agents/agents/index.html.erb +74 -9
- data/app/views/ruby_llm/agents/agents/show.html.erb +18 -378
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
- data/app/views/ruby_llm/agents/executions/show.html.erb +16 -0
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +236 -0
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +76 -0
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +74 -0
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +108 -0
- data/app/views/ruby_llm/agents/workflows/show.html.erb +442 -0
- data/config/routes.rb +1 -0
- data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +110 -0
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +107 -0
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +115 -0
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +108 -0
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +116 -0
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +178 -0
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +109 -0
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +103 -0
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +102 -0
- data/lib/generators/ruby_llm_agents/install_generator.rb +76 -4
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +292 -0
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +121 -0
- data/lib/generators/ruby_llm_agents/templates/add_execution_type_migration.rb.tt +8 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +99 -84
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +42 -40
- data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +26 -0
- data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +50 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +26 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +38 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +139 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +21 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +49 -0
- data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +53 -0
- data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +44 -0
- data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +41 -0
- data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +45 -0
- data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +35 -0
- data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +47 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +50 -0
- data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +44 -0
- data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +38 -0
- data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +33 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +228 -0
- data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +131 -0
- data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +255 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +102 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +282 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +228 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +110 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +212 -0
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +227 -0
- data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +251 -0
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +300 -0
- data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +56 -0
- data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +51 -0
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +107 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +152 -1
- data/lib/ruby_llm/agents/audio/speaker.rb +553 -0
- data/lib/ruby_llm/agents/audio/transcriber.rb +669 -0
- data/lib/ruby_llm/agents/base_agent.rb +675 -0
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +181 -0
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +274 -0
- data/lib/ruby_llm/agents/core/base.rb +135 -0
- data/lib/ruby_llm/agents/core/configuration.rb +981 -0
- data/lib/ruby_llm/agents/core/errors.rb +150 -0
- data/lib/ruby_llm/agents/{instrumentation.rb → core/instrumentation.rb} +22 -1
- data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
- data/lib/ruby_llm/agents/{version.rb → core/version.rb} +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +110 -0
- data/lib/ruby_llm/agents/dsl/caching.rb +142 -0
- data/lib/ruby_llm/agents/dsl/reliability.rb +307 -0
- data/lib/ruby_llm/agents/dsl.rb +41 -0
- data/lib/ruby_llm/agents/image/analyzer/dsl.rb +130 -0
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +402 -0
- data/lib/ruby_llm/agents/image/analyzer.rb +90 -0
- data/lib/ruby_llm/agents/image/background_remover/dsl.rb +154 -0
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +240 -0
- data/lib/ruby_llm/agents/image/background_remover.rb +89 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +91 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +165 -0
- data/lib/ruby_llm/agents/image/editor/dsl.rb +56 -0
- data/lib/ruby_llm/agents/image/editor/execution.rb +207 -0
- data/lib/ruby_llm/agents/image/editor.rb +92 -0
- data/lib/ruby_llm/agents/image/generator/active_storage_support.rb +127 -0
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +95 -0
- data/lib/ruby_llm/agents/image/generator/pricing.rb +353 -0
- data/lib/ruby_llm/agents/image/generator/templates.rb +124 -0
- data/lib/ruby_llm/agents/image/generator.rb +455 -0
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +213 -0
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +382 -0
- data/lib/ruby_llm/agents/image/pipeline.rb +97 -0
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +148 -0
- data/lib/ruby_llm/agents/image/transformer/execution.rb +223 -0
- data/lib/ruby_llm/agents/image/transformer.rb +95 -0
- data/lib/ruby_llm/agents/image/upscaler/dsl.rb +83 -0
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +219 -0
- data/lib/ruby_llm/agents/image/upscaler.rb +81 -0
- data/lib/ruby_llm/agents/image/variator/dsl.rb +62 -0
- data/lib/ruby_llm/agents/image/variator/execution.rb +189 -0
- data/lib/ruby_llm/agents/image/variator.rb +80 -0
- data/lib/ruby_llm/agents/{alert_manager.rb → infrastructure/alert_manager.rb} +17 -22
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +145 -0
- data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +149 -0
- data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +68 -0
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +279 -0
- data/lib/ruby_llm/agents/infrastructure/budget_tracker.rb +275 -0
- data/lib/ruby_llm/agents/{execution_logger_job.rb → infrastructure/execution_logger_job.rb} +17 -1
- data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/executor.rb +2 -1
- data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/retry_strategy.rb +9 -3
- data/lib/ruby_llm/agents/{reliability.rb → infrastructure/reliability.rb} +11 -21
- data/lib/ruby_llm/agents/pipeline/builder.rb +215 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +255 -0
- data/lib/ruby_llm/agents/pipeline/executor.rb +86 -0
- data/lib/ruby_llm/agents/pipeline/middleware/base.rb +124 -0
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +95 -0
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +171 -0
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +415 -0
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +276 -0
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +196 -0
- data/lib/ruby_llm/agents/pipeline.rb +68 -0
- data/lib/ruby_llm/agents/{engine.rb → rails/engine.rb} +79 -11
- data/lib/ruby_llm/agents/results/background_removal_result.rb +286 -0
- data/lib/ruby_llm/agents/{result.rb → results/base.rb} +73 -1
- data/lib/ruby_llm/agents/results/embedding_result.rb +243 -0
- data/lib/ruby_llm/agents/results/image_analysis_result.rb +314 -0
- data/lib/ruby_llm/agents/results/image_edit_result.rb +250 -0
- data/lib/ruby_llm/agents/results/image_generation_result.rb +346 -0
- data/lib/ruby_llm/agents/results/image_pipeline_result.rb +399 -0
- data/lib/ruby_llm/agents/results/image_transform_result.rb +251 -0
- data/lib/ruby_llm/agents/results/image_upscale_result.rb +255 -0
- data/lib/ruby_llm/agents/results/image_variation_result.rb +237 -0
- data/lib/ruby_llm/agents/results/moderation_result.rb +158 -0
- data/lib/ruby_llm/agents/results/speech_result.rb +338 -0
- data/lib/ruby_llm/agents/results/transcription_result.rb +408 -0
- data/lib/ruby_llm/agents/text/embedder.rb +444 -0
- data/lib/ruby_llm/agents/text/moderator.rb +237 -0
- data/lib/ruby_llm/agents/workflow/async.rb +220 -0
- data/lib/ruby_llm/agents/workflow/async_executor.rb +156 -0
- data/lib/ruby_llm/agents/{workflow.rb → workflow/orchestrator.rb} +6 -5
- data/lib/ruby_llm/agents/workflow/parallel.rb +34 -17
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +185 -0
- data/lib/ruby_llm/agents.rb +86 -20
- metadata +172 -34
- data/lib/ruby_llm/agents/base/caching.rb +0 -40
- data/lib/ruby_llm/agents/base/cost_calculation.rb +0 -105
- data/lib/ruby_llm/agents/base/dsl.rb +0 -324
- data/lib/ruby_llm/agents/base/execution.rb +0 -366
- data/lib/ruby_llm/agents/base/reliability_dsl.rb +0 -82
- data/lib/ruby_llm/agents/base/reliability_execution.rb +0 -136
- data/lib/ruby_llm/agents/base/response_building.rb +0 -86
- data/lib/ruby_llm/agents/base/tool_tracking.rb +0 -57
- data/lib/ruby_llm/agents/base.rb +0 -210
- data/lib/ruby_llm/agents/budget_tracker.rb +0 -733
- data/lib/ruby_llm/agents/configuration.rb +0 -394
- /data/lib/ruby_llm/agents/{deprecations.rb → core/deprecations.rb} +0 -0
- /data/lib/ruby_llm/agents/{inflections.rb → core/inflections.rb} +0 -0
- /data/lib/ruby_llm/agents/{resolved_config.rb → core/resolved_config.rb} +0 -0
- /data/lib/ruby_llm/agents/{attempt_tracker.rb → infrastructure/attempt_tracker.rb} +0 -0
- /data/lib/ruby_llm/agents/{cache_helper.rb → infrastructure/cache_helper.rb} +0 -0
- /data/lib/ruby_llm/agents/{circuit_breaker.rb → infrastructure/circuit_breaker.rb} +0 -0
- /data/lib/ruby_llm/agents/{redactor.rb → infrastructure/redactor.rb} +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/breaker_manager.rb +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/execution_constraints.rb +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/fallback_routing.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 34342a67c548fae89c92f0cddb75ad820dfe6a334050f806c1acab9b9b84bf29
|
|
4
|
+
data.tar.gz: a7d57596ff69a931e28e36ee680976557bc9f704f4d69f2296988226ed3ff9ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 89f6e9f8c05ef128e2c55652b69f5a6e986499d1811fa5ed4a92958eeea1ad129359e39762a3d72124f83b9b38981201216eb5eb45849d2cb265471b1ed45ee7
|
|
7
|
+
data.tar.gz: a0d1d64b761f94051b18ad0d79585678b536145a8bdfcc05d22cf4e905a4a913848901e85f3dd2ef37b755f14ceb25372c766219af69fa5f12932c108b9e6086
|
data/README.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="./logo_dark.png">
|
|
3
|
+
<source media="(prefers-color-scheme: light)" srcset="./logo_light.png">
|
|
4
|
+
<img alt="RubyLLM::Agents" src="./logo_light.png">
|
|
5
|
+
</picture>
|
|
6
|
+
|
|
1
7
|
# RubyLLM::Agents
|
|
2
8
|
|
|
3
9
|
> **AI Agents:** For comprehensive documentation optimized for AI consumption, see [LLMS.txt](LLMS.txt)
|
|
@@ -29,13 +35,17 @@ Build intelligent AI agents in Ruby with a clean DSL, automatic execution tracki
|
|
|
29
35
|
| **Cost Analytics** | Track spending by agent, model, tenant, and time period | [Analytics](https://github.com/adham90/ruby_llm-agents/wiki/Execution-Tracking) |
|
|
30
36
|
| **Reliability** | Automatic retries, model fallbacks, circuit breakers with block DSL | [Reliability](https://github.com/adham90/ruby_llm-agents/wiki/Reliability) |
|
|
31
37
|
| **Budget Controls** | Daily/monthly limits with hard and soft enforcement | [Budgets](https://github.com/adham90/ruby_llm-agents/wiki/Budget-Controls) |
|
|
32
|
-
| **Multi-Tenancy** | Per-tenant budgets, circuit breakers, and execution isolation | [Multi-Tenancy](https://github.com/adham90/ruby_llm-agents/wiki/Multi-Tenancy) |
|
|
38
|
+
| **Multi-Tenancy** | Per-tenant API keys, budgets, circuit breakers, and execution isolation | [Multi-Tenancy](https://github.com/adham90/ruby_llm-agents/wiki/Multi-Tenancy) |
|
|
33
39
|
| **Workflows** | Pipelines, parallel execution, conditional routers | [Workflows](https://github.com/adham90/ruby_llm-agents/wiki/Workflows) |
|
|
40
|
+
| **Async/Fiber** | Concurrent execution with Ruby fibers for high-throughput workloads | [Async](https://github.com/adham90/ruby_llm-agents/wiki/Async-Fiber) |
|
|
34
41
|
| **Dashboard** | Real-time Turbo-powered monitoring UI | [Dashboard](https://github.com/adham90/ruby_llm-agents/wiki/Dashboard) |
|
|
35
42
|
| **Streaming** | Real-time response streaming with TTFT tracking | [Streaming](https://github.com/adham90/ruby_llm-agents/wiki/Streaming) |
|
|
36
43
|
| **Conversation History** | Multi-turn conversations with message history | [Conversation History](https://github.com/adham90/ruby_llm-agents/wiki/Conversation-History) |
|
|
37
44
|
| **Attachments** | Images, PDFs, and multimodal support | [Attachments](https://github.com/adham90/ruby_llm-agents/wiki/Attachments) |
|
|
38
45
|
| **PII Redaction** | Automatic sensitive data protection | [Security](https://github.com/adham90/ruby_llm-agents/wiki/PII-Redaction) |
|
|
46
|
+
| **Content Moderation** | Input/output safety checks with OpenAI moderation API | [Moderation](https://github.com/adham90/ruby_llm-agents/wiki/Moderation) |
|
|
47
|
+
| **Embeddings** | Vector embeddings with batching, caching, and preprocessing | [Embeddings](https://github.com/adham90/ruby_llm-agents/wiki/Embeddings) |
|
|
48
|
+
| **Image Operations** | Generation, analysis, editing, pipelines with cost tracking | [Images](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) |
|
|
39
49
|
| **Alerts** | Slack, webhook, and custom notifications | [Alerts](https://github.com/adham90/ruby_llm-agents/wiki/Alerts) |
|
|
40
50
|
|
|
41
51
|
## Quick Start
|
|
@@ -69,21 +79,23 @@ rails generate ruby_llm_agents:agent SearchIntent query:required
|
|
|
69
79
|
```
|
|
70
80
|
|
|
71
81
|
```ruby
|
|
72
|
-
# app/agents/search_intent_agent.rb
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
# app/llm/agents/search_intent_agent.rb
|
|
83
|
+
module LLM
|
|
84
|
+
class SearchIntentAgent < ApplicationAgent
|
|
85
|
+
model "gpt-4o"
|
|
86
|
+
temperature 0.0
|
|
76
87
|
|
|
77
|
-
|
|
88
|
+
param :query, required: true
|
|
78
89
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
def user_prompt
|
|
91
|
+
"Extract search intent from: #{query}"
|
|
92
|
+
end
|
|
82
93
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
def schema
|
|
95
|
+
@schema ||= RubyLLM::Schema.create do
|
|
96
|
+
string :refined_query, description: "Cleaned search query"
|
|
97
|
+
array :filters, of: :string, description: "Extracted filters"
|
|
98
|
+
end
|
|
87
99
|
end
|
|
88
100
|
end
|
|
89
101
|
end
|
|
@@ -92,7 +104,7 @@ end
|
|
|
92
104
|
### Call the Agent
|
|
93
105
|
|
|
94
106
|
```ruby
|
|
95
|
-
result = SearchIntentAgent.call(query: "red summer dress under $50")
|
|
107
|
+
result = LLM::SearchIntentAgent.call(query: "red summer dress under $50")
|
|
96
108
|
|
|
97
109
|
result.content # => { refined_query: "red dress", filters: ["color:red", "price:<50"] }
|
|
98
110
|
result.total_cost # => 0.00025
|
|
@@ -117,6 +129,108 @@ result = ChatAgent.call(
|
|
|
117
129
|
|
|
118
130
|
See [Conversation History](https://github.com/adham90/ruby_llm-agents/wiki/Conversation-History) for more patterns.
|
|
119
131
|
|
|
132
|
+
### Embeddings
|
|
133
|
+
|
|
134
|
+
Generate vector embeddings for semantic search, RAG, and similarity matching:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
rails generate ruby_llm_agents:embedder Document
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
# app/llm/text/embedders/document_embedder.rb
|
|
142
|
+
module LLM
|
|
143
|
+
module Text
|
|
144
|
+
class DocumentEmbedder < ApplicationEmbedder
|
|
145
|
+
model "text-embedding-3-small"
|
|
146
|
+
dimensions 512
|
|
147
|
+
batch_size 100
|
|
148
|
+
cache_for 1.week
|
|
149
|
+
|
|
150
|
+
# Optional: preprocess text before embedding
|
|
151
|
+
def preprocess(text)
|
|
152
|
+
text.strip.downcase.gsub(/\s+/, ' ')
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
# Single text embedding
|
|
161
|
+
result = LLM::Text::DocumentEmbedder.call(text: "Hello world")
|
|
162
|
+
result.vector # => [0.123, -0.456, ...]
|
|
163
|
+
result.dimensions # => 512
|
|
164
|
+
result.total_tokens # => 2
|
|
165
|
+
|
|
166
|
+
# Batch embedding
|
|
167
|
+
result = LLM::Text::DocumentEmbedder.call(texts: ["Hello", "World", "Ruby"])
|
|
168
|
+
result.vectors # => [[...], [...], [...]]
|
|
169
|
+
result.count # => 3
|
|
170
|
+
|
|
171
|
+
# With progress callback for large batches
|
|
172
|
+
LLM::Text::DocumentEmbedder.call(texts: large_array) do |batch_result, index|
|
|
173
|
+
puts "Processed batch #{index + 1}"
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Features:
|
|
178
|
+
- **Configurable dimensions** - Reduce dimensions for efficient storage
|
|
179
|
+
- **Batch processing** - Embed multiple texts in optimized API calls
|
|
180
|
+
- **Caching** - Cache embeddings to reduce API costs
|
|
181
|
+
- **Preprocessing** - Clean and normalize text before embedding
|
|
182
|
+
- **Execution tracking** - All embeddings logged with tokens and costs
|
|
183
|
+
|
|
184
|
+
See [Embeddings](https://github.com/adham90/ruby_llm-agents/wiki/Embeddings) for more patterns.
|
|
185
|
+
|
|
186
|
+
### Image Operations
|
|
187
|
+
|
|
188
|
+
Comprehensive image capabilities with generation, analysis, editing, and pipelines:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Generate image generators, analyzers, pipelines, and more
|
|
192
|
+
rails generate ruby_llm_agents:image_generator Logo
|
|
193
|
+
rails generate ruby_llm_agents:image_analyzer Product
|
|
194
|
+
rails generate ruby_llm_agents:background_remover Photo
|
|
195
|
+
rails generate ruby_llm_agents:image_pipeline Ecommerce --steps generate,upscale,analyze
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
# Image Generation - create images from prompts
|
|
200
|
+
result = LLM::Image::LogoGenerator.call(prompt: "tech startup logo")
|
|
201
|
+
result.url # => "https://..."
|
|
202
|
+
result.save("logo.png")
|
|
203
|
+
|
|
204
|
+
# Image Analysis - extract captions, tags, objects, colors
|
|
205
|
+
result = LLM::Image::ProductAnalyzer.call(image: "product.jpg")
|
|
206
|
+
result.caption # => "Red sneaker on white background"
|
|
207
|
+
result.tags # => ["sneaker", "red", "footwear"]
|
|
208
|
+
result.colors # => [{ hex: "#FF0000", percentage: 30 }]
|
|
209
|
+
|
|
210
|
+
# Background Removal - extract subjects with transparency
|
|
211
|
+
result = LLM::Image::PhotoBackgroundRemover.call(image: "portrait.jpg")
|
|
212
|
+
result.save("portrait_transparent.png")
|
|
213
|
+
|
|
214
|
+
# Image Pipelines - chain multiple operations
|
|
215
|
+
result = LLM::Image::EcommercePipeline.call(
|
|
216
|
+
prompt: "professional laptop photo",
|
|
217
|
+
high_quality: true
|
|
218
|
+
)
|
|
219
|
+
result.final_image # => Final processed image
|
|
220
|
+
result.total_cost # => Combined cost of all steps
|
|
221
|
+
result.step(:analyze).tags # => Access individual step results
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Features:
|
|
225
|
+
- **8 image operation classes** - Generator, Analyzer, Editor, Transformer, Upscaler, Variator, BackgroundRemover, Pipeline
|
|
226
|
+
- **Prompt templates** - Consistent styling with preset templates
|
|
227
|
+
- **Content policy** - Validate prompts with configurable safety levels
|
|
228
|
+
- **Cost tracking** - Dynamic pricing with execution logging
|
|
229
|
+
- **Image Pipelines** - Chain operations into automated workflows
|
|
230
|
+
- **ActiveStorage** - Attach images directly to Rails models
|
|
231
|
+
|
|
232
|
+
See [Image Operations](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) for full documentation.
|
|
233
|
+
|
|
120
234
|
## Documentation
|
|
121
235
|
|
|
122
236
|
> **Note:** Wiki content lives in the [`wiki/`](wiki/) folder. To sync changes to the [GitHub Wiki](https://github.com/adham90/ruby_llm-agents/wiki), run `./scripts/sync-wiki.sh`.
|
|
@@ -129,8 +243,12 @@ See [Conversation History](https://github.com/adham90/ruby_llm-agents/wiki/Conve
|
|
|
129
243
|
| [Workflows](https://github.com/adham90/ruby_llm-agents/wiki/Workflows) | Pipelines, parallel execution, routers |
|
|
130
244
|
| [Budget Controls](https://github.com/adham90/ruby_llm-agents/wiki/Budget-Controls) | Spending limits, alerts, enforcement |
|
|
131
245
|
| [Multi-Tenancy](https://github.com/adham90/ruby_llm-agents/wiki/Multi-Tenancy) | Per-tenant budgets, isolation, configuration |
|
|
246
|
+
| [Async/Fiber](https://github.com/adham90/ruby_llm-agents/wiki/Async-Fiber) | Concurrent execution with Ruby fibers |
|
|
132
247
|
| [Testing Agents](https://github.com/adham90/ruby_llm-agents/wiki/Testing-Agents) | RSpec patterns, mocking, dry_run mode |
|
|
133
248
|
| [Error Handling](https://github.com/adham90/ruby_llm-agents/wiki/Error-Handling) | Error types, recovery patterns |
|
|
249
|
+
| [Moderation](https://github.com/adham90/ruby_llm-agents/wiki/Moderation) | Content moderation for input/output safety |
|
|
250
|
+
| [Embeddings](https://github.com/adham90/ruby_llm-agents/wiki/Embeddings) | Vector embeddings, batching, caching, preprocessing |
|
|
251
|
+
| [Image Generation](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) | Text-to-image, templates, content policy, cost tracking |
|
|
134
252
|
| [Dashboard](https://github.com/adham90/ruby_llm-agents/wiki/Dashboard) | Setup, authentication, analytics |
|
|
135
253
|
| [Production](https://github.com/adham90/ruby_llm-agents/wiki/Production-Deployment) | Deployment best practices, background jobs |
|
|
136
254
|
| [API Reference](https://github.com/adham90/ruby_llm-agents/wiki/API-Reference) | Complete class documentation |
|
|
@@ -141,28 +259,31 @@ See [Conversation History](https://github.com/adham90/ruby_llm-agents/wiki/Conve
|
|
|
141
259
|
Build resilient agents with built-in fault tolerance:
|
|
142
260
|
|
|
143
261
|
```ruby
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
circuit_breaker errors: 10, within: 60, cooldown: 300
|
|
152
|
-
total_timeout 30
|
|
153
|
-
|
|
154
|
-
# Option 2: Grouped reliability block (equivalent to above)
|
|
155
|
-
reliability do
|
|
262
|
+
# app/llm/agents/reliable_agent.rb
|
|
263
|
+
module LLM
|
|
264
|
+
class ReliableAgent < ApplicationAgent
|
|
265
|
+
model "gpt-4o"
|
|
266
|
+
description "A resilient agent with automatic retries and fallbacks"
|
|
267
|
+
|
|
268
|
+
# Option 1: Individual DSL methods
|
|
156
269
|
retries max: 3, backoff: :exponential
|
|
157
270
|
fallback_models "gpt-4o-mini", "claude-3-5-sonnet"
|
|
158
271
|
circuit_breaker errors: 10, within: 60, cooldown: 300
|
|
159
272
|
total_timeout 30
|
|
160
|
-
end
|
|
161
273
|
|
|
162
|
-
|
|
274
|
+
# Option 2: Grouped reliability block (equivalent to above)
|
|
275
|
+
reliability do
|
|
276
|
+
retries max: 3, backoff: :exponential
|
|
277
|
+
fallback_models "gpt-4o-mini", "claude-3-5-sonnet"
|
|
278
|
+
circuit_breaker errors: 10, within: 60, cooldown: 300
|
|
279
|
+
total_timeout 30
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
param :query, required: true
|
|
163
283
|
|
|
164
|
-
|
|
165
|
-
|
|
284
|
+
def user_prompt
|
|
285
|
+
query
|
|
286
|
+
end
|
|
166
287
|
end
|
|
167
288
|
end
|
|
168
289
|
```
|
|
@@ -172,7 +293,7 @@ end
|
|
|
172
293
|
The result object provides detailed execution metadata:
|
|
173
294
|
|
|
174
295
|
```ruby
|
|
175
|
-
result = ReliableAgent.call(query: "test")
|
|
296
|
+
result = LLM::ReliableAgent.call(query: "test")
|
|
176
297
|
|
|
177
298
|
# Basic response
|
|
178
299
|
result.content # => { ... }
|
|
@@ -233,6 +354,43 @@ result = SupportRouter.call(message: user_input)
|
|
|
233
354
|
result.routed_to # :support, :sales, or :default
|
|
234
355
|
```
|
|
235
356
|
|
|
357
|
+
## Async/Fiber Concurrency
|
|
358
|
+
|
|
359
|
+
Run multiple agents concurrently with minimal resources using Ruby's Fiber scheduler:
|
|
360
|
+
|
|
361
|
+
```ruby
|
|
362
|
+
# Add async gem to Gemfile
|
|
363
|
+
gem 'async'
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
require 'async'
|
|
368
|
+
|
|
369
|
+
# Run agents concurrently - non-blocking I/O
|
|
370
|
+
Async do
|
|
371
|
+
results = RubyLLM::Agents::Async.batch([
|
|
372
|
+
[SentimentAgent, { input: "I love this!" }],
|
|
373
|
+
[SummaryAgent, { input: "Long text..." }],
|
|
374
|
+
[CategoryAgent, { input: "Product review" }]
|
|
375
|
+
], max_concurrent: 10)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Process large collections efficiently
|
|
379
|
+
Async do
|
|
380
|
+
results = RubyLLM::Agents::Async.each(texts, max_concurrent: 20) do |text|
|
|
381
|
+
AnalyzerAgent.call(input: text)
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Benefits:
|
|
387
|
+
- **100x less memory** - Fibers use ~10KB vs ~1MB per thread
|
|
388
|
+
- **Shared connections** - Single database connection for all fibers
|
|
389
|
+
- **Auto-detection** - Parallel workflows automatically use fibers in async context
|
|
390
|
+
- **Non-blocking retries** - Backoff delays don't block other operations
|
|
391
|
+
|
|
392
|
+
See [Async/Fiber](https://github.com/adham90/ruby_llm-agents/wiki/Async-Fiber) for more patterns.
|
|
393
|
+
|
|
236
394
|
## Cost & Budget Controls
|
|
237
395
|
|
|
238
396
|
Track and limit LLM spending:
|
|
@@ -30,6 +30,16 @@ module RubyLLM
|
|
|
30
30
|
@agents = all_items.reject { |a| a[:is_workflow] }
|
|
31
31
|
@workflows = all_items.select { |a| a[:is_workflow] }
|
|
32
32
|
|
|
33
|
+
# Group agents by type for sub-tabs
|
|
34
|
+
@agents_by_type = {
|
|
35
|
+
agent: @agents.select { |a| a[:agent_type] == "agent" },
|
|
36
|
+
embedder: @agents.select { |a| a[:agent_type] == "embedder" },
|
|
37
|
+
moderator: @agents.select { |a| a[:agent_type] == "moderator" },
|
|
38
|
+
speaker: @agents.select { |a| a[:agent_type] == "speaker" },
|
|
39
|
+
transcriber: @agents.select { |a| a[:agent_type] == "transcriber" },
|
|
40
|
+
image_generator: @agents.select { |a| a[:agent_type] == "image_generator" }
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
# Group workflows by type for sub-tabs
|
|
34
44
|
@workflows_by_type = {
|
|
35
45
|
pipeline: @workflows.select { |w| w[:workflow_type] == "pipeline" },
|
|
@@ -44,6 +54,7 @@ module RubyLLM
|
|
|
44
54
|
Rails.logger.error("[RubyLLM::Agents] Error loading agents: #{e.message}")
|
|
45
55
|
@agents = []
|
|
46
56
|
@workflows = []
|
|
57
|
+
@agents_by_type = { agent: [], embedder: [], moderator: [], speaker: [], transcriber: [], image_generator: [] }
|
|
47
58
|
@workflows_by_type = { pipeline: [], parallel: [], router: [] }
|
|
48
59
|
@agent_count = 0
|
|
49
60
|
@workflow_count = 0
|
|
@@ -69,7 +80,8 @@ module RubyLLM
|
|
|
69
80
|
|
|
70
81
|
if @agent_class
|
|
71
82
|
load_agent_config
|
|
72
|
-
|
|
83
|
+
# Only load circuit breaker status for base agents
|
|
84
|
+
load_circuit_breaker_status if @agent_type_kind == "agent"
|
|
73
85
|
end
|
|
74
86
|
rescue StandardError => e
|
|
75
87
|
Rails.logger.error("[RubyLLM::Agents] Error loading agent #{@agent_type}: #{e.message}")
|
|
@@ -207,26 +219,134 @@ module RubyLLM
|
|
|
207
219
|
#
|
|
208
220
|
# Extracts DSL-configured values from the agent class for display.
|
|
209
221
|
# Only called if the agent class still exists.
|
|
222
|
+
# Detects agent type and loads appropriate config.
|
|
210
223
|
#
|
|
211
224
|
# @return [void]
|
|
212
225
|
def load_agent_config
|
|
226
|
+
@agent_type_kind = AgentRegistry.send(:detect_agent_type, @agent_class)
|
|
227
|
+
|
|
228
|
+
# Common config for all types
|
|
213
229
|
@config = {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
version: @agent_class.version,
|
|
218
|
-
description: @agent_class.respond_to?(:description) ? @agent_class.description : nil,
|
|
219
|
-
timeout: @agent_class.timeout,
|
|
220
|
-
cache_enabled: @agent_class.cache_enabled?,
|
|
221
|
-
cache_ttl: @agent_class.cache_ttl,
|
|
222
|
-
params: @agent_class.params,
|
|
223
|
-
|
|
224
|
-
# Reliability configuration
|
|
225
|
-
retries: @agent_class.retries,
|
|
226
|
-
fallback_models: @agent_class.fallback_models,
|
|
227
|
-
total_timeout: @agent_class.total_timeout,
|
|
228
|
-
circuit_breaker: @agent_class.circuit_breaker_config
|
|
230
|
+
model: safe_config_call(:model),
|
|
231
|
+
version: safe_config_call(:version) || "N/A",
|
|
232
|
+
description: safe_config_call(:description)
|
|
229
233
|
}
|
|
234
|
+
|
|
235
|
+
# Type-specific config
|
|
236
|
+
case @agent_type_kind
|
|
237
|
+
when "embedder"
|
|
238
|
+
load_embedder_config
|
|
239
|
+
when "moderator"
|
|
240
|
+
load_moderator_config
|
|
241
|
+
when "speaker"
|
|
242
|
+
load_speaker_config
|
|
243
|
+
when "transcriber"
|
|
244
|
+
load_transcriber_config
|
|
245
|
+
when "image_generator"
|
|
246
|
+
load_image_generator_config
|
|
247
|
+
else
|
|
248
|
+
load_base_agent_config
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Loads configuration specific to Base agents
|
|
253
|
+
#
|
|
254
|
+
# @return [void]
|
|
255
|
+
def load_base_agent_config
|
|
256
|
+
@config.merge!(
|
|
257
|
+
temperature: safe_config_call(:temperature),
|
|
258
|
+
timeout: safe_config_call(:timeout),
|
|
259
|
+
cache_enabled: safe_config_call(:cache_enabled?) || false,
|
|
260
|
+
cache_ttl: safe_config_call(:cache_ttl),
|
|
261
|
+
params: safe_config_call(:params) || {},
|
|
262
|
+
retries: safe_config_call(:retries),
|
|
263
|
+
fallback_models: safe_config_call(:fallback_models),
|
|
264
|
+
total_timeout: safe_config_call(:total_timeout),
|
|
265
|
+
circuit_breaker: safe_config_call(:circuit_breaker_config)
|
|
266
|
+
)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Loads configuration specific to Embedders
|
|
270
|
+
#
|
|
271
|
+
# @return [void]
|
|
272
|
+
def load_embedder_config
|
|
273
|
+
@config.merge!(
|
|
274
|
+
dimensions: safe_config_call(:dimensions),
|
|
275
|
+
batch_size: safe_config_call(:batch_size),
|
|
276
|
+
cache_enabled: safe_config_call(:cache_enabled?) || false,
|
|
277
|
+
cache_ttl: safe_config_call(:cache_ttl)
|
|
278
|
+
)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Loads configuration specific to Moderators
|
|
282
|
+
#
|
|
283
|
+
# @return [void]
|
|
284
|
+
def load_moderator_config
|
|
285
|
+
@config.merge!(
|
|
286
|
+
threshold: safe_config_call(:threshold),
|
|
287
|
+
categories: safe_config_call(:categories)
|
|
288
|
+
)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Loads configuration specific to Speakers
|
|
292
|
+
#
|
|
293
|
+
# @return [void]
|
|
294
|
+
def load_speaker_config
|
|
295
|
+
@config.merge!(
|
|
296
|
+
provider: safe_config_call(:provider),
|
|
297
|
+
voice: safe_config_call(:voice),
|
|
298
|
+
voice_id: safe_config_call(:voice_id),
|
|
299
|
+
speed: safe_config_call(:speed),
|
|
300
|
+
output_format: safe_config_call(:output_format),
|
|
301
|
+
streaming: safe_config_call(:streaming?),
|
|
302
|
+
ssml_enabled: safe_config_call(:ssml_enabled?),
|
|
303
|
+
cache_enabled: safe_config_call(:cache_enabled?) || false,
|
|
304
|
+
cache_ttl: safe_config_call(:cache_ttl)
|
|
305
|
+
)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Loads configuration specific to Transcribers
|
|
309
|
+
#
|
|
310
|
+
# @return [void]
|
|
311
|
+
def load_transcriber_config
|
|
312
|
+
@config.merge!(
|
|
313
|
+
language: safe_config_call(:language),
|
|
314
|
+
output_format: safe_config_call(:output_format),
|
|
315
|
+
include_timestamps: safe_config_call(:include_timestamps),
|
|
316
|
+
cache_enabled: safe_config_call(:cache_enabled?) || false,
|
|
317
|
+
cache_ttl: safe_config_call(:cache_ttl),
|
|
318
|
+
fallback_models: safe_config_call(:fallback_models)
|
|
319
|
+
)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Loads configuration specific to ImageGenerators
|
|
323
|
+
#
|
|
324
|
+
# @return [void]
|
|
325
|
+
def load_image_generator_config
|
|
326
|
+
@config.merge!(
|
|
327
|
+
size: safe_config_call(:size),
|
|
328
|
+
quality: safe_config_call(:quality),
|
|
329
|
+
style: safe_config_call(:style),
|
|
330
|
+
content_policy: safe_config_call(:content_policy),
|
|
331
|
+
template: safe_config_call(:template_string),
|
|
332
|
+
negative_prompt: safe_config_call(:negative_prompt),
|
|
333
|
+
seed: safe_config_call(:seed),
|
|
334
|
+
guidance_scale: safe_config_call(:guidance_scale),
|
|
335
|
+
steps: safe_config_call(:steps),
|
|
336
|
+
cache_enabled: safe_config_call(:cache_enabled?) || false,
|
|
337
|
+
cache_ttl: safe_config_call(:cache_ttl)
|
|
338
|
+
)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Safely calls a method on the agent class, returning nil on error
|
|
342
|
+
#
|
|
343
|
+
# @param method [Symbol] The method to call
|
|
344
|
+
# @return [Object, nil] The result or nil if error
|
|
345
|
+
def safe_config_call(method)
|
|
346
|
+
return nil unless @agent_class&.respond_to?(method)
|
|
347
|
+
@agent_class.public_send(method)
|
|
348
|
+
rescue StandardError
|
|
349
|
+
nil
|
|
230
350
|
end
|
|
231
351
|
|
|
232
352
|
# Loads circuit breaker status for the agent's models
|
|
@@ -53,10 +53,19 @@ module RubyLLM
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
# Builds per-agent comparison statistics
|
|
56
|
+
# Builds per-agent comparison statistics for all agent types
|
|
57
|
+
#
|
|
58
|
+
# Creates separate instance variables for each agent type:
|
|
59
|
+
# - @agent_stats: Base agents
|
|
60
|
+
# - @embedder_stats: Embedders
|
|
61
|
+
# - @transcriber_stats: Transcribers
|
|
62
|
+
# - @speaker_stats: Speakers
|
|
63
|
+
# - @image_generator_stats: Image generators
|
|
64
|
+
# - @moderator_stats: Moderators
|
|
65
|
+
# - @workflow_stats: Workflows
|
|
57
66
|
#
|
|
58
67
|
# @param base_scope [ActiveRecord::Relation] Base scope to filter from
|
|
59
|
-
# @return [Array<Hash>] Array of
|
|
68
|
+
# @return [Array<Hash>] Array of base agent stats (for backward compatibility)
|
|
60
69
|
def build_agent_comparison(base_scope = Execution)
|
|
61
70
|
scope = base_scope.last_n_days(@days)
|
|
62
71
|
agent_types = scope.distinct.pluck(:agent_type)
|
|
@@ -67,26 +76,37 @@ module RubyLLM
|
|
|
67
76
|
total_cost = agent_scope.sum(:total_cost) || 0
|
|
68
77
|
successful = agent_scope.successful.count
|
|
69
78
|
|
|
70
|
-
# Detect
|
|
79
|
+
# Detect agent type using AgentRegistry
|
|
71
80
|
agent_class = AgentRegistry.find(agent_type)
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
detected_type = AgentRegistry.send(:detect_agent_type, agent_class)
|
|
82
|
+
|
|
83
|
+
# Get workflow type if applicable
|
|
84
|
+
workflow_type = detected_type == "workflow" ? detect_workflow_type(agent_class) : nil
|
|
74
85
|
|
|
75
86
|
{
|
|
76
87
|
agent_type: agent_type,
|
|
88
|
+
detected_type: detected_type,
|
|
77
89
|
executions: count,
|
|
78
90
|
total_cost: total_cost,
|
|
79
91
|
avg_cost: count > 0 ? (total_cost / count).round(6) : 0,
|
|
80
92
|
avg_duration_ms: agent_scope.average(:duration_ms)&.round || 0,
|
|
81
93
|
success_rate: count > 0 ? (successful.to_f / count * 100).round(1) : 0,
|
|
82
|
-
is_workflow:
|
|
94
|
+
is_workflow: detected_type == "workflow",
|
|
83
95
|
workflow_type: workflow_type
|
|
84
96
|
}
|
|
85
97
|
end.sort_by { |a| -(a[:total_cost] || 0) }
|
|
86
98
|
|
|
87
|
-
# Split
|
|
88
|
-
@
|
|
89
|
-
all_stats.
|
|
99
|
+
# Split stats by agent type for 7-tab display
|
|
100
|
+
@agent_stats = all_stats.select { |a| a[:detected_type] == "agent" }
|
|
101
|
+
@embedder_stats = all_stats.select { |a| a[:detected_type] == "embedder" }
|
|
102
|
+
@transcriber_stats = all_stats.select { |a| a[:detected_type] == "transcriber" }
|
|
103
|
+
@speaker_stats = all_stats.select { |a| a[:detected_type] == "speaker" }
|
|
104
|
+
@image_generator_stats = all_stats.select { |a| a[:detected_type] == "image_generator" }
|
|
105
|
+
@moderator_stats = all_stats.select { |a| a[:detected_type] == "moderator" }
|
|
106
|
+
@workflow_stats = all_stats.select { |a| a[:detected_type] == "workflow" }
|
|
107
|
+
|
|
108
|
+
# Return base agents for backward compatibility
|
|
109
|
+
@agent_stats
|
|
90
110
|
end
|
|
91
111
|
|
|
92
112
|
# Detects workflow type from class hierarchy
|