ruby_llm-agents 0.4.0 → 1.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +225 -34
- data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +214 -0
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
- data/app/controllers/ruby_llm/agents/{settings_controller.rb → system_config_controller.rb} +3 -3
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +109 -0
- 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/api_configuration.rb +386 -0
- data/app/models/ruby_llm/agents/execution.rb +3 -0
- data/app/models/ruby_llm/agents/tenant_budget.rb +112 -14
- data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +5 -30
- 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/api_configurations/_api_key_field.html.erb +34 -0
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +288 -0
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +95 -0
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +97 -0
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +211 -0
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +179 -0
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +1 -1
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
- data/app/views/ruby_llm/agents/executions/show.html.erb +98 -0
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
- data/app/views/ruby_llm/agents/{settings → system_config}/show.html.erb +1 -1
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +150 -0
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +13 -0
- data/app/views/ruby_llm/agents/tenants/index.html.erb +129 -0
- data/app/views/ruby_llm/agents/tenants/show.html.erb +374 -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 +13 -1
- data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +100 -0
- 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/create_api_configurations_migration.rb.tt +90 -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} +93 -4
- data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
- data/lib/ruby_llm/agents/core/resolved_config.rb +348 -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 -10
- 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 +189 -35
- 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 -283
- 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 -209
- data/lib/ruby_llm/agents/budget_tracker.rb +0 -471
- data/lib/ruby_llm/agents/configuration.rb +0 -357
- /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/{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
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
# Controller for managing API configurations
|
|
6
|
+
#
|
|
7
|
+
# Provides CRUD operations for global and tenant-specific API
|
|
8
|
+
# configurations, including API keys and connection settings.
|
|
9
|
+
#
|
|
10
|
+
# @see ApiConfiguration
|
|
11
|
+
# @api private
|
|
12
|
+
class ApiConfigurationsController < ApplicationController
|
|
13
|
+
before_action :ensure_table_exists
|
|
14
|
+
before_action :set_global_config, only: [:show, :edit, :update]
|
|
15
|
+
before_action :set_tenant_config, only: [:tenant, :edit_tenant, :update_tenant]
|
|
16
|
+
|
|
17
|
+
# Displays the global API configuration
|
|
18
|
+
#
|
|
19
|
+
# @return [void]
|
|
20
|
+
def show
|
|
21
|
+
@resolved = ApiConfiguration.resolve
|
|
22
|
+
@provider_statuses = @resolved.provider_statuses_with_source
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Renders the edit form for global configuration
|
|
26
|
+
#
|
|
27
|
+
# @return [void]
|
|
28
|
+
def edit
|
|
29
|
+
@resolved = ApiConfiguration.resolve
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Updates the global API configuration
|
|
33
|
+
#
|
|
34
|
+
# @return [void]
|
|
35
|
+
def update
|
|
36
|
+
if @config.update(api_configuration_params)
|
|
37
|
+
log_configuration_change(@config, "global")
|
|
38
|
+
redirect_to edit_api_configuration_path, notice: "API configuration updated successfully"
|
|
39
|
+
else
|
|
40
|
+
render :edit, status: :unprocessable_entity
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Displays tenant-specific API configuration
|
|
45
|
+
#
|
|
46
|
+
# @return [void]
|
|
47
|
+
def tenant
|
|
48
|
+
@resolved = ApiConfiguration.resolve(tenant_id: params[:tenant_id])
|
|
49
|
+
@provider_statuses = @resolved.provider_statuses_with_source
|
|
50
|
+
@tenant_budget = TenantBudget.for_tenant(params[:tenant_id])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Renders the edit form for tenant configuration
|
|
54
|
+
#
|
|
55
|
+
# @return [void]
|
|
56
|
+
def edit_tenant
|
|
57
|
+
@resolved = ApiConfiguration.resolve(tenant_id: @tenant_id)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Updates a tenant-specific API configuration
|
|
61
|
+
#
|
|
62
|
+
# @return [void]
|
|
63
|
+
def update_tenant
|
|
64
|
+
if @config.update(api_configuration_params)
|
|
65
|
+
log_configuration_change(@config, "tenant:#{params[:tenant_id]}")
|
|
66
|
+
redirect_to edit_tenant_api_configuration_path(params[:tenant_id]),
|
|
67
|
+
notice: "Tenant API configuration updated successfully"
|
|
68
|
+
else
|
|
69
|
+
render :edit_tenant, status: :unprocessable_entity
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Tests API key validity for a specific provider
|
|
74
|
+
# (Optional - can be used for AJAX validation)
|
|
75
|
+
#
|
|
76
|
+
# @return [void]
|
|
77
|
+
def test_connection
|
|
78
|
+
provider = params[:provider]
|
|
79
|
+
api_key = params[:api_key]
|
|
80
|
+
|
|
81
|
+
result = test_provider_connection(provider, api_key)
|
|
82
|
+
|
|
83
|
+
render json: {
|
|
84
|
+
success: result[:success],
|
|
85
|
+
message: result[:message],
|
|
86
|
+
models: result[:models]
|
|
87
|
+
}
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
render json: { success: false, message: e.message }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# Ensures the api_configurations table exists
|
|
95
|
+
def ensure_table_exists
|
|
96
|
+
return if ApiConfiguration.table_exists?
|
|
97
|
+
|
|
98
|
+
flash[:alert] = "API configurations table not found. Run the generator: rails generate ruby_llm_agents:api_configuration"
|
|
99
|
+
redirect_to root_path
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Sets the global configuration (creates if not exists)
|
|
103
|
+
def set_global_config
|
|
104
|
+
@config = ApiConfiguration.global
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Sets the tenant-specific configuration (creates if not exists)
|
|
108
|
+
def set_tenant_config
|
|
109
|
+
@tenant_id = params[:tenant_id]
|
|
110
|
+
raise ActionController::RoutingError, "Tenant ID required" if @tenant_id.blank?
|
|
111
|
+
|
|
112
|
+
@config = ApiConfiguration.for_tenant!(@tenant_id)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Strong parameters for API configuration
|
|
116
|
+
#
|
|
117
|
+
# @return [ActionController::Parameters]
|
|
118
|
+
def api_configuration_params
|
|
119
|
+
params.require(:api_configuration).permit(
|
|
120
|
+
# API Keys
|
|
121
|
+
:openai_api_key,
|
|
122
|
+
:anthropic_api_key,
|
|
123
|
+
:gemini_api_key,
|
|
124
|
+
:deepseek_api_key,
|
|
125
|
+
:mistral_api_key,
|
|
126
|
+
:perplexity_api_key,
|
|
127
|
+
:openrouter_api_key,
|
|
128
|
+
:gpustack_api_key,
|
|
129
|
+
:xai_api_key,
|
|
130
|
+
:ollama_api_key,
|
|
131
|
+
# AWS Bedrock
|
|
132
|
+
:bedrock_api_key,
|
|
133
|
+
:bedrock_secret_key,
|
|
134
|
+
:bedrock_session_token,
|
|
135
|
+
:bedrock_region,
|
|
136
|
+
# Vertex AI
|
|
137
|
+
:vertexai_credentials,
|
|
138
|
+
:vertexai_project_id,
|
|
139
|
+
:vertexai_location,
|
|
140
|
+
# Endpoints
|
|
141
|
+
:openai_api_base,
|
|
142
|
+
:gemini_api_base,
|
|
143
|
+
:ollama_api_base,
|
|
144
|
+
:gpustack_api_base,
|
|
145
|
+
:xai_api_base,
|
|
146
|
+
# OpenAI Options
|
|
147
|
+
:openai_organization_id,
|
|
148
|
+
:openai_project_id,
|
|
149
|
+
# Default Models
|
|
150
|
+
:default_model,
|
|
151
|
+
:default_embedding_model,
|
|
152
|
+
:default_image_model,
|
|
153
|
+
:default_moderation_model,
|
|
154
|
+
# Connection Settings
|
|
155
|
+
:request_timeout,
|
|
156
|
+
:max_retries,
|
|
157
|
+
:retry_interval,
|
|
158
|
+
:retry_backoff_factor,
|
|
159
|
+
:retry_interval_randomness,
|
|
160
|
+
:http_proxy,
|
|
161
|
+
# Inheritance
|
|
162
|
+
:inherit_global_defaults
|
|
163
|
+
).tap do |permitted|
|
|
164
|
+
# Remove blank API keys to prevent overwriting with empty values
|
|
165
|
+
# This allows users to submit forms without touching existing keys
|
|
166
|
+
ApiConfiguration::API_KEY_ATTRIBUTES.each do |key|
|
|
167
|
+
permitted.delete(key) if permitted[key].blank?
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Logs configuration changes for audit purposes
|
|
173
|
+
#
|
|
174
|
+
# @param config [ApiConfiguration] The configuration that changed
|
|
175
|
+
# @param scope [String] The scope identifier
|
|
176
|
+
def log_configuration_change(config, scope)
|
|
177
|
+
changed_fields = config.previous_changes.keys.reject { |k| k.end_with?("_at") }
|
|
178
|
+
return if changed_fields.empty?
|
|
179
|
+
|
|
180
|
+
# Mask sensitive fields in the log
|
|
181
|
+
masked_changes = changed_fields.map do |field|
|
|
182
|
+
if field.include?("api_key") || field.include?("secret") || field.include?("credentials")
|
|
183
|
+
"#{field}: [REDACTED]"
|
|
184
|
+
else
|
|
185
|
+
"#{field}: #{config.previous_changes[field].last}"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
Rails.logger.info(
|
|
190
|
+
"[RubyLLM::Agents] API configuration updated for #{scope}: #{masked_changes.join(', ')}"
|
|
191
|
+
)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Tests connection to a specific provider
|
|
195
|
+
#
|
|
196
|
+
# @param provider [String] Provider key
|
|
197
|
+
# @param api_key [String] API key to test
|
|
198
|
+
# @return [Hash] Test result with success, message, and models
|
|
199
|
+
def test_provider_connection(provider, api_key)
|
|
200
|
+
# This is a placeholder - actual implementation would depend on
|
|
201
|
+
# RubyLLM's ability to list models or make a test request
|
|
202
|
+
case provider
|
|
203
|
+
when "openai"
|
|
204
|
+
# Example: Try to list models
|
|
205
|
+
{ success: true, message: "Connection successful", models: [] }
|
|
206
|
+
when "anthropic"
|
|
207
|
+
{ success: true, message: "Connection successful", models: [] }
|
|
208
|
+
else
|
|
209
|
+
{ success: false, message: "Provider not supported for testing" }
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
@@ -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
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Agents
|
|
5
|
-
# Controller for displaying
|
|
5
|
+
# Controller for displaying system configuration
|
|
6
6
|
#
|
|
7
7
|
# Shows all configuration options from RubyLLM::Agents.configuration
|
|
8
8
|
# in a read-only dashboard view.
|
|
9
9
|
#
|
|
10
10
|
# @api private
|
|
11
|
-
class
|
|
12
|
-
# Displays the
|
|
11
|
+
class SystemConfigController < ApplicationController
|
|
12
|
+
# Displays the system configuration
|
|
13
13
|
#
|
|
14
14
|
# @return [void]
|
|
15
15
|
def show
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
# Controller for managing tenant budgets
|
|
6
|
+
#
|
|
7
|
+
# Provides CRUD operations for viewing and editing tenant budget
|
|
8
|
+
# configurations, including cost limits and token limits.
|
|
9
|
+
#
|
|
10
|
+
# @see TenantBudget For budget configuration model
|
|
11
|
+
# @api private
|
|
12
|
+
class TenantsController < ApplicationController
|
|
13
|
+
# Lists all tenant budgets
|
|
14
|
+
#
|
|
15
|
+
# @return [void]
|
|
16
|
+
def index
|
|
17
|
+
@tenants = TenantBudget.order(:name, :tenant_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Shows a single tenant's budget details
|
|
21
|
+
#
|
|
22
|
+
# @return [void]
|
|
23
|
+
def show
|
|
24
|
+
@tenant = TenantBudget.find(params[:id])
|
|
25
|
+
@executions = tenant_executions(@tenant.tenant_id).recent.limit(10)
|
|
26
|
+
@usage_stats = calculate_usage_stats(@tenant)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Renders the edit form for a tenant budget
|
|
30
|
+
#
|
|
31
|
+
# @return [void]
|
|
32
|
+
def edit
|
|
33
|
+
@tenant = TenantBudget.find(params[:id])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Updates a tenant budget
|
|
37
|
+
#
|
|
38
|
+
# @return [void]
|
|
39
|
+
def update
|
|
40
|
+
@tenant = TenantBudget.find(params[:id])
|
|
41
|
+
if @tenant.update(tenant_params)
|
|
42
|
+
redirect_to tenant_path(@tenant), notice: "Tenant updated successfully"
|
|
43
|
+
else
|
|
44
|
+
render :edit, status: :unprocessable_entity
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Strong parameters for tenant budget
|
|
51
|
+
#
|
|
52
|
+
# @return [ActionController::Parameters] Permitted parameters
|
|
53
|
+
def tenant_params
|
|
54
|
+
params.require(:tenant_budget).permit(
|
|
55
|
+
:name, :daily_limit, :monthly_limit,
|
|
56
|
+
:daily_token_limit, :monthly_token_limit,
|
|
57
|
+
:enforcement
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns executions scoped to a specific tenant
|
|
62
|
+
#
|
|
63
|
+
# @param tenant_id [String] The tenant identifier
|
|
64
|
+
# @return [ActiveRecord::Relation] Executions for the tenant
|
|
65
|
+
def tenant_executions(tenant_id)
|
|
66
|
+
Execution.by_tenant(tenant_id)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Calculates usage statistics for a tenant
|
|
70
|
+
#
|
|
71
|
+
# @param tenant [TenantBudget] The tenant budget record
|
|
72
|
+
# @return [Hash] Usage statistics
|
|
73
|
+
def calculate_usage_stats(tenant)
|
|
74
|
+
scope = tenant_executions(tenant.tenant_id)
|
|
75
|
+
today_scope = scope.where("created_at >= ?", Time.current.beginning_of_day)
|
|
76
|
+
month_scope = scope.where("created_at >= ?", Time.current.beginning_of_month)
|
|
77
|
+
|
|
78
|
+
daily_spend = today_scope.sum(:total_cost) || 0
|
|
79
|
+
monthly_spend = month_scope.sum(:total_cost) || 0
|
|
80
|
+
daily_tokens = today_scope.sum(:total_tokens) || 0
|
|
81
|
+
monthly_tokens = month_scope.sum(:total_tokens) || 0
|
|
82
|
+
|
|
83
|
+
{
|
|
84
|
+
daily_spend: daily_spend,
|
|
85
|
+
monthly_spend: monthly_spend,
|
|
86
|
+
daily_tokens: daily_tokens,
|
|
87
|
+
monthly_tokens: monthly_tokens,
|
|
88
|
+
daily_spend_percentage: percentage_used(daily_spend, tenant.effective_daily_limit),
|
|
89
|
+
monthly_spend_percentage: percentage_used(monthly_spend, tenant.effective_monthly_limit),
|
|
90
|
+
daily_token_percentage: percentage_used(daily_tokens, tenant.effective_daily_token_limit),
|
|
91
|
+
monthly_token_percentage: percentage_used(monthly_tokens, tenant.effective_monthly_token_limit),
|
|
92
|
+
total_executions: scope.count,
|
|
93
|
+
total_cost: scope.sum(:total_cost) || 0,
|
|
94
|
+
total_tokens: scope.sum(:total_tokens) || 0
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Calculates percentage used
|
|
99
|
+
#
|
|
100
|
+
# @param current [Numeric] Current usage
|
|
101
|
+
# @param limit [Numeric, nil] The limit
|
|
102
|
+
# @return [Float] Percentage used (0-100+)
|
|
103
|
+
def percentage_used(current, limit)
|
|
104
|
+
return 0 if limit.nil? || limit.to_f <= 0
|
|
105
|
+
(current.to_f / limit.to_f * 100).round(1)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|