ruby_llm-agents 1.3.4 → 2.1.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/README.md +112 -336
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
- data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
- data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
- data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
- data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
- data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
- data/app/models/ruby_llm/agents/execution.rb +52 -12
- data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
- data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
- data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
- data/app/models/ruby_llm/agents/tenant.rb +2 -3
- data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
- data/app/views/layouts/ruby_llm/agents/application.html.erb +89 -252
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
- data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
- data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
- data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
- data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
- data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
- data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
- data/app/views/ruby_llm/agents/executions/show.html.erb +526 -1037
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
- data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
- data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
- data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
- data/config/routes.rb +0 -13
- data/lib/generators/ruby_llm_agents/install_generator.rb +13 -17
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
- data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +33 -12
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
- data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
- data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +77 -259
- data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
- data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
- data/lib/ruby_llm/agents/base_agent.rb +54 -23
- data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
- data/lib/ruby_llm/agents/core/base.rb +23 -55
- data/lib/ruby_llm/agents/core/configuration.rb +97 -117
- data/lib/ruby_llm/agents/core/errors.rb +0 -58
- data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
- data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +157 -17
- data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
- data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -2
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
- data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/editor.rb +0 -1
- data/lib/ruby_llm/agents/image/generator.rb +0 -21
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/transformer.rb +0 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
- data/lib/ruby_llm/agents/rails/engine.rb +6 -6
- data/lib/ruby_llm/agents/results/base.rb +1 -49
- data/lib/ruby_llm/agents/text/embedder.rb +0 -1
- data/lib/ruby_llm/agents.rb +1 -9
- data/lib/tasks/ruby_llm_agents.rake +34 -0
- metadata +14 -83
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
- data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
- data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
- data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
- data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
- data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
- data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
- data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
- data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
- data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
- data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
- data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
- data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
- data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
- data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
- data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
- data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
- data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
- data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
- data/lib/ruby_llm/agents/text/moderator.rb +0 -237
- data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
- data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
- data/lib/ruby_llm/agents/workflow/async.rb +0 -220
- data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
- data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
- data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
- data/lib/ruby_llm/agents/workflow/result.rb +0 -592
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
- data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b341c701b87662c5947fec756ba5a925759f8179700683dda02c1b30951d3213
|
|
4
|
+
data.tar.gz: bec52ce05c7958b6954b8f6df23a8a9058ac2a9f900decdb858ed108623ff5af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fefed6d947fb4e9dc8e85a683c72754062d22c016c1de61b9922d49ae9a54cef9e84e638cadb1000e221fdd7616bfd1764fbf47bf5ba11404126acedcebe7c03
|
|
7
|
+
data.tar.gz: f93588c36b8057878b951c8411e00c2cd94ecc6189be8d33f70cb49e7d7e81098755606fbc3dcb6b2b8dfe85efc5c043a3d1884b7ecf48fc185a4863b4d0f16d
|
data/README.md
CHANGED
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
# RubyLLM::Agents
|
|
8
8
|
|
|
9
|
-
> **AI Agents:** For comprehensive documentation optimized for AI consumption, see [LLMS.txt](LLMS.txt)
|
|
10
|
-
|
|
11
9
|
> **Production-ready Rails engine for building, managing, and monitoring LLM-powered AI agents**
|
|
12
10
|
|
|
13
11
|
[](https://rubygems.org/gems/ruby_llm-agents)
|
|
@@ -23,88 +21,28 @@ Build intelligent AI agents in Ruby with a clean DSL, automatic execution tracki
|
|
|
23
21
|
- **Rails-Native** - Seamlessly integrates with your Rails app: models, jobs, caching, and Hotwire
|
|
24
22
|
- **Production-Ready** - Built-in retries, model fallbacks, circuit breakers, and budget limits
|
|
25
23
|
- **Full Observability** - Track every execution with costs, tokens, duration, and errors
|
|
26
|
-
- **Workflow Orchestration** - Compose agents into pipelines, parallel tasks, and conditional routers
|
|
27
24
|
- **Zero Lock-in** - Works with any LLM provider supported by RubyLLM
|
|
28
25
|
|
|
29
|
-
##
|
|
30
|
-
|
|
31
|
-
| Feature | Description | Docs |
|
|
32
|
-
|---------|-------------|------|
|
|
33
|
-
| **Agent DSL** | Declarative configuration with model, temperature, parameters, description | [Agent DSL](https://github.com/adham90/ruby_llm-agents/wiki/Agent-DSL) |
|
|
34
|
-
| **Execution Tracking** | Automatic logging with token usage, cost analytics, and fallback tracking | [Tracking](https://github.com/adham90/ruby_llm-agents/wiki/Execution-Tracking) |
|
|
35
|
-
| **Cost Analytics** | Track spending by agent, model, tenant, and time period | [Analytics](https://github.com/adham90/ruby_llm-agents/wiki/Execution-Tracking) |
|
|
36
|
-
| **Reliability** | Automatic retries, model fallbacks, circuit breakers with block DSL | [Reliability](https://github.com/adham90/ruby_llm-agents/wiki/Reliability) |
|
|
37
|
-
| **Budget Controls** | Daily/monthly limits with hard and soft enforcement | [Budgets](https://github.com/adham90/ruby_llm-agents/wiki/Budget-Controls) |
|
|
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) |
|
|
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) |
|
|
41
|
-
| **Dashboard** | Real-time Turbo-powered monitoring UI | [Dashboard](https://github.com/adham90/ruby_llm-agents/wiki/Dashboard) |
|
|
42
|
-
| **Streaming** | Real-time response streaming with TTFT tracking | [Streaming](https://github.com/adham90/ruby_llm-agents/wiki/Streaming) |
|
|
43
|
-
| **Conversation History** | Multi-turn conversations with message history | [Conversation History](https://github.com/adham90/ruby_llm-agents/wiki/Conversation-History) |
|
|
44
|
-
| **Attachments** | Images, PDFs, and multimodal support | [Attachments](https://github.com/adham90/ruby_llm-agents/wiki/Attachments) |
|
|
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) |
|
|
49
|
-
| **Alerts** | Slack, webhook, and custom notifications | [Alerts](https://github.com/adham90/ruby_llm-agents/wiki/Alerts) |
|
|
50
|
-
|
|
51
|
-
## Quick Start
|
|
52
|
-
|
|
53
|
-
### Installation
|
|
26
|
+
## Show Me the Code
|
|
54
27
|
|
|
55
28
|
```ruby
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
# .env
|
|
70
|
-
OPENAI_API_KEY=sk-...
|
|
71
|
-
ANTHROPIC_API_KEY=sk-ant-...
|
|
72
|
-
GOOGLE_API_KEY=...
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Create Your First Agent
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
rails generate ruby_llm_agents:agent SearchIntent query:required
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
```ruby
|
|
82
|
-
# app/llm/agents/search_intent_agent.rb
|
|
83
|
-
module LLM
|
|
84
|
-
class SearchIntentAgent < ApplicationAgent
|
|
85
|
-
model "gpt-4o"
|
|
86
|
-
temperature 0.0
|
|
87
|
-
|
|
88
|
-
param :query, required: true
|
|
89
|
-
|
|
90
|
-
def user_prompt
|
|
91
|
-
"Extract search intent from: #{query}"
|
|
92
|
-
end
|
|
93
|
-
|
|
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
|
|
99
|
-
end
|
|
29
|
+
# app/agents/search_intent_agent.rb
|
|
30
|
+
class SearchIntentAgent < ApplicationAgent
|
|
31
|
+
model "gpt-4o"
|
|
32
|
+
temperature 0.0
|
|
33
|
+
|
|
34
|
+
# Prompts with {placeholder} syntax - params auto-registered
|
|
35
|
+
system "You are a search intent analyzer. Extract structured data from queries."
|
|
36
|
+
prompt "Extract search intent from: {query}"
|
|
37
|
+
|
|
38
|
+
# Structured output with returns DSL
|
|
39
|
+
returns do
|
|
40
|
+
string :refined_query, description: "Cleaned search query"
|
|
41
|
+
array :filters, of: :string, description: "Extracted filters"
|
|
100
42
|
end
|
|
101
43
|
end
|
|
102
|
-
```
|
|
103
44
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```ruby
|
|
107
|
-
result = LLM::SearchIntentAgent.call(query: "red summer dress under $50")
|
|
45
|
+
result = SearchIntentAgent.call(query: "red summer dress under $50")
|
|
108
46
|
|
|
109
47
|
result.content # => { refined_query: "red dress", filters: ["color:red", "price:<50"] }
|
|
110
48
|
result.total_cost # => 0.00025
|
|
@@ -112,11 +50,8 @@ result.total_tokens # => 150
|
|
|
112
50
|
result.duration_ms # => 850
|
|
113
51
|
```
|
|
114
52
|
|
|
115
|
-
### Multi-Turn Conversations
|
|
116
|
-
|
|
117
|
-
Build chatbots and conversational agents with message history:
|
|
118
|
-
|
|
119
53
|
```ruby
|
|
54
|
+
# Multi-turn conversations
|
|
120
55
|
result = ChatAgent.call(
|
|
121
56
|
query: "What's my name?",
|
|
122
57
|
messages: [
|
|
@@ -127,112 +62,133 @@ result = ChatAgent.call(
|
|
|
127
62
|
# => "Your name is Alice!"
|
|
128
63
|
```
|
|
129
64
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
65
|
+
```ruby
|
|
66
|
+
# Resilient agents with automatic retries and fallbacks
|
|
67
|
+
class ReliableAgent < ApplicationAgent
|
|
68
|
+
model "gpt-4o"
|
|
133
69
|
|
|
134
|
-
|
|
70
|
+
prompt "{query}"
|
|
135
71
|
|
|
136
|
-
|
|
137
|
-
|
|
72
|
+
on_failure do
|
|
73
|
+
retries times: 3, backoff: :exponential
|
|
74
|
+
fallback to: ["gpt-4o-mini", "claude-3-5-sonnet"]
|
|
75
|
+
circuit_breaker after: 10, within: 60, cooldown: 5.minutes
|
|
76
|
+
timeout 30
|
|
77
|
+
end
|
|
78
|
+
end
|
|
138
79
|
```
|
|
139
80
|
|
|
140
81
|
```ruby
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
82
|
+
# Vector embeddings for semantic search and RAG
|
|
83
|
+
# app/agents/embedders/document_embedder.rb
|
|
84
|
+
module Embedders
|
|
85
|
+
class DocumentEmbedder < ApplicationEmbedder
|
|
86
|
+
model "text-embedding-3-small"
|
|
87
|
+
dimensions 512
|
|
88
|
+
cache_for 1.week
|
|
155
89
|
end
|
|
156
90
|
end
|
|
157
|
-
```
|
|
158
91
|
|
|
159
|
-
|
|
160
|
-
# Single text embedding
|
|
161
|
-
result = LLM::Text::DocumentEmbedder.call(text: "Hello world")
|
|
92
|
+
result = Embedders::DocumentEmbedder.call(text: "Hello world")
|
|
162
93
|
result.vector # => [0.123, -0.456, ...]
|
|
163
94
|
result.dimensions # => 512
|
|
164
|
-
result.total_tokens # => 2
|
|
165
95
|
|
|
166
96
|
# Batch embedding
|
|
167
|
-
result =
|
|
97
|
+
result = Embedders::DocumentEmbedder.call(texts: ["Hello", "World", "Ruby"])
|
|
168
98
|
result.vectors # => [[...], [...], [...]]
|
|
169
|
-
|
|
99
|
+
```
|
|
170
100
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
101
|
+
```ruby
|
|
102
|
+
# Image generation, analysis, and pipelines
|
|
103
|
+
# app/agents/images/logo_generator.rb
|
|
104
|
+
module Images
|
|
105
|
+
class LogoGenerator < ApplicationImageGenerator
|
|
106
|
+
model "gpt-image-1"
|
|
107
|
+
size "1024x1024"
|
|
108
|
+
quality "hd"
|
|
109
|
+
style "vivid"
|
|
110
|
+
template "Professional logo design: {prompt}. Minimalist, scalable."
|
|
111
|
+
end
|
|
174
112
|
end
|
|
113
|
+
|
|
114
|
+
result = Images::LogoGenerator.call(prompt: "tech startup logo")
|
|
115
|
+
result.url # => "https://..."
|
|
116
|
+
result.save("logo.png")
|
|
175
117
|
```
|
|
176
118
|
|
|
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
|
|
119
|
+
## Features
|
|
183
120
|
|
|
184
|
-
|
|
121
|
+
| Feature | Description | Docs |
|
|
122
|
+
|---------|-------------|------|
|
|
123
|
+
| **Agent DSL** | Declarative configuration with model, temperature, parameters, description | [Agent DSL](https://github.com/adham90/ruby_llm-agents/wiki/Agent-DSL) |
|
|
124
|
+
| **Execution Tracking** | Automatic logging with token usage, cost analytics, and fallback tracking | [Tracking](https://github.com/adham90/ruby_llm-agents/wiki/Execution-Tracking) |
|
|
125
|
+
| **Cost Analytics** | Track spending by agent, model, tenant, and time period | [Analytics](https://github.com/adham90/ruby_llm-agents/wiki/Execution-Tracking) |
|
|
126
|
+
| **Reliability** | Automatic retries, model fallbacks, circuit breakers with block DSL | [Reliability](https://github.com/adham90/ruby_llm-agents/wiki/Reliability) |
|
|
127
|
+
| **Budget Controls** | Daily/monthly limits with hard and soft enforcement | [Budgets](https://github.com/adham90/ruby_llm-agents/wiki/Budget-Controls) |
|
|
128
|
+
| **Multi-Tenancy** | Per-tenant API keys, budgets, circuit breakers, and execution isolation | [Multi-Tenancy](https://github.com/adham90/ruby_llm-agents/wiki/Multi-Tenancy) |
|
|
129
|
+
| **Async/Fiber** | Concurrent execution with Ruby fibers for high-throughput workloads | [Async](https://github.com/adham90/ruby_llm-agents/wiki/Async-Fiber) |
|
|
130
|
+
| **Dashboard** | Real-time Turbo-powered monitoring UI | [Dashboard](https://github.com/adham90/ruby_llm-agents/wiki/Dashboard) |
|
|
131
|
+
| **Streaming** | Real-time response streaming with TTFT tracking | [Streaming](https://github.com/adham90/ruby_llm-agents/wiki/Streaming) |
|
|
132
|
+
| **Conversation History** | Multi-turn conversations with message history | [Conversation History](https://github.com/adham90/ruby_llm-agents/wiki/Conversation-History) |
|
|
133
|
+
| **Attachments** | Images, PDFs, and multimodal support | [Attachments](https://github.com/adham90/ruby_llm-agents/wiki/Attachments) |
|
|
134
|
+
| **Embeddings** | Vector embeddings with batching, caching, and preprocessing | [Embeddings](https://github.com/adham90/ruby_llm-agents/wiki/Embeddings) |
|
|
135
|
+
| **Image Operations** | Generation, analysis, editing, pipelines with cost tracking | [Images](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) |
|
|
136
|
+
| **Alerts** | Slack, webhook, and custom notifications | [Alerts](https://github.com/adham90/ruby_llm-agents/wiki/Alerts) |
|
|
185
137
|
|
|
186
|
-
|
|
138
|
+
## Quick Start
|
|
139
|
+
|
|
140
|
+
### Installation
|
|
187
141
|
|
|
188
|
-
|
|
142
|
+
```ruby
|
|
143
|
+
# Gemfile
|
|
144
|
+
gem "ruby_llm-agents"
|
|
145
|
+
```
|
|
189
146
|
|
|
190
147
|
```bash
|
|
191
|
-
|
|
192
|
-
rails generate ruby_llm_agents:
|
|
193
|
-
rails
|
|
194
|
-
rails generate ruby_llm_agents:background_remover Photo
|
|
195
|
-
rails generate ruby_llm_agents:image_pipeline Ecommerce --steps generate,upscale,analyze
|
|
148
|
+
bundle install
|
|
149
|
+
rails generate ruby_llm_agents:install
|
|
150
|
+
rails db:migrate
|
|
196
151
|
```
|
|
197
152
|
|
|
198
|
-
|
|
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")
|
|
153
|
+
### Configure API Keys
|
|
203
154
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
155
|
+
```bash
|
|
156
|
+
# .env
|
|
157
|
+
OPENAI_API_KEY=sk-...
|
|
158
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
159
|
+
GOOGLE_API_KEY=...
|
|
160
|
+
```
|
|
209
161
|
|
|
210
|
-
|
|
211
|
-
result = LLM::Image::PhotoBackgroundRemover.call(image: "portrait.jpg")
|
|
212
|
-
result.save("portrait_transparent.png")
|
|
162
|
+
### Generate an Agent
|
|
213
163
|
|
|
214
|
-
|
|
215
|
-
|
|
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
|
|
164
|
+
```bash
|
|
165
|
+
rails generate ruby_llm_agents:agent SearchIntent query:required
|
|
222
166
|
```
|
|
223
167
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
168
|
+
This creates `app/agents/search_intent_agent.rb` with the agent class ready to customize.
|
|
169
|
+
|
|
170
|
+
### Mount the Dashboard
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
# config/routes.rb
|
|
174
|
+
mount RubyLLM::Agents::Engine => "/agents"
|
|
175
|
+
```
|
|
231
176
|
|
|
232
|
-
|
|
177
|
+
<table>
|
|
178
|
+
<tr>
|
|
179
|
+
<td><img src="screenshots/dashboard.png" alt="Dashboard Overview" width="400"></td>
|
|
180
|
+
<td><img src="screenshots/agents.png" alt="Agent Registry" width="400"></td>
|
|
181
|
+
</tr>
|
|
182
|
+
<tr>
|
|
183
|
+
<td><img src="screenshots/executions.png" alt="Execution Log" width="400"></td>
|
|
184
|
+
<td><img src="screenshots/tenants.png" alt="Multi-Tenancy" width="400"></td>
|
|
185
|
+
</tr>
|
|
186
|
+
</table>
|
|
233
187
|
|
|
234
188
|
## Documentation
|
|
235
189
|
|
|
190
|
+
> **AI Agents:** For comprehensive documentation optimized for AI consumption, see [LLMS.txt](LLMS.txt)
|
|
191
|
+
|
|
236
192
|
> **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`.
|
|
237
193
|
|
|
238
194
|
| Guide | Description |
|
|
@@ -240,203 +196,23 @@ See [Image Operations](https://github.com/adham90/ruby_llm-agents/wiki/Image-Gen
|
|
|
240
196
|
| [Getting Started](https://github.com/adham90/ruby_llm-agents/wiki/Getting-Started) | Installation, configuration, first agent |
|
|
241
197
|
| [Agent DSL](https://github.com/adham90/ruby_llm-agents/wiki/Agent-DSL) | All DSL options: model, temperature, params, caching, description |
|
|
242
198
|
| [Reliability](https://github.com/adham90/ruby_llm-agents/wiki/Reliability) | Retries, fallbacks, circuit breakers, timeouts, reliability block |
|
|
243
|
-
| [Workflows](https://github.com/adham90/ruby_llm-agents/wiki/Workflows) | Pipelines, parallel execution, routers |
|
|
244
199
|
| [Budget Controls](https://github.com/adham90/ruby_llm-agents/wiki/Budget-Controls) | Spending limits, alerts, enforcement |
|
|
245
200
|
| [Multi-Tenancy](https://github.com/adham90/ruby_llm-agents/wiki/Multi-Tenancy) | Per-tenant budgets, isolation, configuration |
|
|
246
201
|
| [Async/Fiber](https://github.com/adham90/ruby_llm-agents/wiki/Async-Fiber) | Concurrent execution with Ruby fibers |
|
|
247
202
|
| [Testing Agents](https://github.com/adham90/ruby_llm-agents/wiki/Testing-Agents) | RSpec patterns, mocking, dry_run mode |
|
|
248
203
|
| [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
204
|
| [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,
|
|
205
|
+
| [Image Generation](https://github.com/adham90/ruby_llm-agents/wiki/Image-Generation) | Text-to-image, templates, pipelines, cost tracking |
|
|
252
206
|
| [Dashboard](https://github.com/adham90/ruby_llm-agents/wiki/Dashboard) | Setup, authentication, analytics |
|
|
253
207
|
| [Production](https://github.com/adham90/ruby_llm-agents/wiki/Production-Deployment) | Deployment best practices, background jobs |
|
|
254
208
|
| [API Reference](https://github.com/adham90/ruby_llm-agents/wiki/API-Reference) | Complete class documentation |
|
|
255
209
|
| [Examples](https://github.com/adham90/ruby_llm-agents/wiki/Examples) | Real-world use cases and patterns |
|
|
256
210
|
|
|
257
|
-
## Reliability Features
|
|
258
|
-
|
|
259
|
-
Build resilient agents with built-in fault tolerance:
|
|
260
|
-
|
|
261
|
-
```ruby
|
|
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
|
|
269
|
-
retries max: 3, backoff: :exponential
|
|
270
|
-
fallback_models "gpt-4o-mini", "claude-3-5-sonnet"
|
|
271
|
-
circuit_breaker errors: 10, within: 60, cooldown: 300
|
|
272
|
-
total_timeout 30
|
|
273
|
-
|
|
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
|
|
283
|
-
|
|
284
|
-
def user_prompt
|
|
285
|
-
query
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Enhanced Result Object
|
|
292
|
-
|
|
293
|
-
The result object provides detailed execution metadata:
|
|
294
|
-
|
|
295
|
-
```ruby
|
|
296
|
-
result = LLM::ReliableAgent.call(query: "test")
|
|
297
|
-
|
|
298
|
-
# Basic response
|
|
299
|
-
result.content # => { ... }
|
|
300
|
-
result.success? # => true
|
|
301
|
-
|
|
302
|
-
# Reliability info
|
|
303
|
-
result.attempts_count # => 2 (if retry occurred)
|
|
304
|
-
result.used_fallback? # => true (if fallback model used)
|
|
305
|
-
result.chosen_model_id # => "gpt-4o-mini" (actual model used)
|
|
306
|
-
|
|
307
|
-
# Cost & timing
|
|
308
|
-
result.total_cost # => 0.00025
|
|
309
|
-
result.total_tokens # => 150
|
|
310
|
-
result.duration_ms # => 850
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Workflow Orchestration
|
|
314
|
-
|
|
315
|
-
Compose agents into complex workflows:
|
|
316
|
-
|
|
317
|
-
```ruby
|
|
318
|
-
# Sequential pipeline - each step's output feeds the next
|
|
319
|
-
class ContentPipeline < RubyLLM::Agents::Workflow::Pipeline
|
|
320
|
-
timeout 60.seconds
|
|
321
|
-
max_cost 1.00
|
|
322
|
-
|
|
323
|
-
step :classify, agent: ClassifierAgent
|
|
324
|
-
step :enrich, agent: EnricherAgent
|
|
325
|
-
step :format, agent: FormatterAgent, optional: true
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
result = ContentPipeline.call(text: data)
|
|
329
|
-
result.steps[:classify].content # Individual step result
|
|
330
|
-
result.total_cost # Sum of all steps
|
|
331
|
-
|
|
332
|
-
# Parallel execution - run agents concurrently
|
|
333
|
-
class AnalysisPipeline < RubyLLM::Agents::Workflow::Parallel
|
|
334
|
-
fail_fast false # Continue even if a branch fails
|
|
335
|
-
|
|
336
|
-
branch :sentiment, agent: SentimentAgent
|
|
337
|
-
branch :entities, agent: EntityAgent
|
|
338
|
-
branch :summary, agent: SummaryAgent
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
result = AnalysisPipeline.call(text: content)
|
|
342
|
-
result.branches[:sentiment].content # Individual branch result
|
|
343
|
-
|
|
344
|
-
# Conditional routing - dispatch based on classification
|
|
345
|
-
class SupportRouter < RubyLLM::Agents::Workflow::Router
|
|
346
|
-
classifier_model "gpt-4o-mini"
|
|
347
|
-
|
|
348
|
-
route :support, to: SupportAgent, description: "Technical support issues"
|
|
349
|
-
route :sales, to: SalesAgent, description: "Sales and pricing questions"
|
|
350
|
-
route :default, to: GeneralAgent
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
result = SupportRouter.call(message: user_input)
|
|
354
|
-
result.routed_to # :support, :sales, or :default
|
|
355
|
-
```
|
|
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
|
-
|
|
394
|
-
## Cost & Budget Controls
|
|
395
|
-
|
|
396
|
-
Track and limit LLM spending:
|
|
397
|
-
|
|
398
|
-
```ruby
|
|
399
|
-
# config/initializers/ruby_llm_agents.rb
|
|
400
|
-
RubyLLM::Agents.configure do |config|
|
|
401
|
-
config.budgets = {
|
|
402
|
-
global_daily: 100.0, # $100/day limit
|
|
403
|
-
global_monthly: 2000.0, # $2000/month limit
|
|
404
|
-
per_agent_daily: {
|
|
405
|
-
"ExpensiveAgent" => 50.0
|
|
406
|
-
},
|
|
407
|
-
enforcement: :hard # Block when exceeded
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
config.alerts = {
|
|
411
|
-
on_events: [:budget_soft_cap, :budget_hard_cap, :breaker_open],
|
|
412
|
-
slack_webhook_url: ENV['SLACK_WEBHOOK_URL']
|
|
413
|
-
}
|
|
414
|
-
end
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
## Dashboard
|
|
418
|
-
|
|
419
|
-
Mount the real-time monitoring dashboard:
|
|
420
|
-
|
|
421
|
-
```ruby
|
|
422
|
-
# config/routes.rb
|
|
423
|
-
mount RubyLLM::Agents::Engine => "/agents"
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-

|
|
427
|
-
|
|
428
|
-
Features:
|
|
429
|
-
- Execution history with filtering and search
|
|
430
|
-
- Cost analytics by agent, model, and time period
|
|
431
|
-
- Performance trends and charts
|
|
432
|
-
- Token usage breakdowns
|
|
433
|
-
- Error tracking and debugging
|
|
434
|
-
|
|
435
211
|
## Requirements
|
|
436
212
|
|
|
437
213
|
- **Ruby** >= 3.1.0
|
|
438
214
|
- **Rails** >= 7.0
|
|
439
|
-
- **RubyLLM** >= 1.0
|
|
215
|
+
- **RubyLLM** >= 1.11.0
|
|
440
216
|
|
|
441
217
|
## Contributing
|
|
442
218
|
|