activeagent 1.1.0 → 1.3.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +185 -0
  3. data/README.md +58 -14
  4. data/lib/active_agent/base.rb +11 -2
  5. data/lib/active_agent/concerns/delegation.rb +385 -0
  6. data/lib/active_agent/delegation/backend.rb +109 -0
  7. data/lib/active_agent/delegation/budget.rb +138 -0
  8. data/lib/active_agent/delegation/contract.rb +117 -0
  9. data/lib/active_agent/delegation/definition.rb +95 -0
  10. data/lib/active_agent/delegation/ledger.rb +56 -0
  11. data/lib/active_agent/delegation/pricing.rb +106 -0
  12. data/lib/active_agent/delegation/runner.rb +283 -0
  13. data/lib/active_agent/delegation/schema.rb +220 -0
  14. data/lib/active_agent/providers/_base_provider.rb +97 -1
  15. data/lib/active_agent/providers/open_ai/chat_provider.rb +21 -2
  16. data/lib/active_agent/telemetry/configuration.rb +13 -9
  17. data/lib/active_agent/telemetry/instrumentation.rb +38 -1
  18. data/lib/active_agent/telemetry/tool_origin.rb +90 -0
  19. data/lib/active_agent/telemetry.rb +1 -0
  20. data/lib/active_agent/version.rb +1 -1
  21. data/lib/active_agent.rb +1 -5
  22. metadata +31 -32
  23. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb +0 -138
  24. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb +0 -64
  25. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb +0 -129
  26. data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb +0 -123
  27. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb +0 -56
  28. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/application_job.rb +0 -14
  29. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb +0 -49
  30. data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb +0 -65
  31. data/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb +0 -86
  32. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +0 -256
  33. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb +0 -113
  34. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb +0 -208
  35. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb +0 -60
  36. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb +0 -46
  37. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb +0 -125
  38. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb +0 -83
  39. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb +0 -52
  40. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb +0 -169
  41. data/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb +0 -193
  42. data/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +0 -214
  43. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb +0 -117
  44. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb +0 -135
  45. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb +0 -145
  46. data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb +0 -36
  47. data/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb +0 -94
  48. data/lib/active_agent/dashboard/config/routes.rb +0 -19
  49. data/lib/active_agent/dashboard/engine.rb +0 -43
  50. data/lib/active_agent/dashboard.rb +0 -161
  51. data/lib/generators/active_agent/dashboard/install_generator.rb +0 -92
  52. data/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb +0 -67
  53. data/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb +0 -46
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeagent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Bowen
@@ -109,6 +109,26 @@ dependencies:
109
109
  - - "<="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '9.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: railties
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '7.2'
119
+ - - "<="
120
+ - !ruby/object:Gem::Version
121
+ version: '9.0'
122
+ type: :runtime
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '7.2'
129
+ - - "<="
130
+ - !ruby/object:Gem::Version
131
+ version: '9.0'
112
132
  - !ruby/object:Gem::Dependency
113
133
  name: activeagents-telemetry
114
134
  requirement: !ruby/object:Gem::Requirement
@@ -390,6 +410,7 @@ files:
390
410
  - lib/active_agent/base.rb
391
411
  - lib/active_agent/collector.rb
392
412
  - lib/active_agent/concerns/callbacks.rb
413
+ - lib/active_agent/concerns/delegation.rb
393
414
  - lib/active_agent/concerns/observers.rb
394
415
  - lib/active_agent/concerns/parameterized.rb
395
416
  - lib/active_agent/concerns/preview.rb
@@ -400,34 +421,14 @@ files:
400
421
  - lib/active_agent/concerns/tooling.rb
401
422
  - lib/active_agent/concerns/view.rb
402
423
  - lib/active_agent/configuration.rb
403
- - lib/active_agent/dashboard.rb
404
- - lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb
405
- - lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb
406
- - lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb
407
- - lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb
408
- - lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb
409
- - lib/active_agent/dashboard/app/jobs/active_agent/dashboard/application_job.rb
410
- - lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb
411
- - lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb
412
- - lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb
413
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb
414
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb
415
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb
416
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb
417
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb
418
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb
419
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb
420
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb
421
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb
422
- - lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb
423
- - lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb
424
- - lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb
425
- - lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb
426
- - lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb
427
- - lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb
428
- - lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb
429
- - lib/active_agent/dashboard/config/routes.rb
430
- - lib/active_agent/dashboard/engine.rb
424
+ - lib/active_agent/delegation/backend.rb
425
+ - lib/active_agent/delegation/budget.rb
426
+ - lib/active_agent/delegation/contract.rb
427
+ - lib/active_agent/delegation/definition.rb
428
+ - lib/active_agent/delegation/ledger.rb
429
+ - lib/active_agent/delegation/pricing.rb
430
+ - lib/active_agent/delegation/runner.rb
431
+ - lib/active_agent/delegation/schema.rb
431
432
  - lib/active_agent/deprecator.rb
432
433
  - lib/active_agent/generation.rb
433
434
  - lib/active_agent/generation_job.rb
@@ -551,14 +552,12 @@ files:
551
552
  - lib/active_agent/telemetry/instrumentation.rb
552
553
  - lib/active_agent/telemetry/reporter.rb
553
554
  - lib/active_agent/telemetry/span.rb
555
+ - lib/active_agent/telemetry/tool_origin.rb
554
556
  - lib/active_agent/telemetry/tracer.rb
555
557
  - lib/active_agent/version.rb
556
558
  - lib/activeagent.rb
557
559
  - lib/generators/active_agent/agent/USAGE
558
560
  - lib/generators/active_agent/agent/agent_generator.rb
559
- - lib/generators/active_agent/dashboard/install_generator.rb
560
- - lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb
561
- - lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb
562
561
  - lib/generators/active_agent/install/USAGE
563
562
  - lib/generators/active_agent/install/install_generator.rb
564
563
  - lib/generators/active_agent/templates/active_agent.yml
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- module Api
6
- # Telemetry ingestion endpoint.
7
- #
8
- # Receives traces from ActiveAgent::Telemetry::Reporter and stores them
9
- # for analysis and visualization in the dashboard.
10
- #
11
- # Supports two modes:
12
- # - Local mode: synchronous processing; unauthenticated unless
13
- # ActiveAgent::Dashboard.ingest_api_key is set (set it whenever the
14
- # mount is reachable beyond your own machine)
15
- # - Multi-tenant mode: per-account Bearer token auth, async processing
16
- # via job
17
- #
18
- # @example Local mode request
19
- # POST <mount>/api/traces (e.g. /activeagents/api/traces)
20
- # Content-Type: application/json
21
- #
22
- # {
23
- # "traces": [...],
24
- # "sdk": { "name": "activeagent", "version": "0.5.0" }
25
- # }
26
- #
27
- # @example Multi-tenant mode request
28
- # POST <mount>/api/traces (e.g. /activeagents/api/traces)
29
- # Authorization: Bearer <api_key>
30
- # Content-Type: application/json
31
- #
32
- # {
33
- # "traces": [...],
34
- # "sdk": { "name": "activeagent", "version": "0.5.0" }
35
- # }
36
- #
37
- class TracesController < ActionController::API
38
- before_action :authenticate_api_key!, if: -> { ActiveAgent::Dashboard.multi_tenant? }
39
- before_action :authenticate_ingest_key!, unless: -> { ActiveAgent::Dashboard.multi_tenant? }
40
-
41
- # Maximum traces accepted per request (mirrors
42
- # ProcessTelemetryTracesJob::MAX_TRACES_PER_JOB).
43
- MAX_TRACES_PER_REQUEST = 100
44
-
45
- # POST <mount>/api/traces (e.g. /activeagents/api/traces)
46
- def create
47
- traces = Array(params[:traces]).take(MAX_TRACES_PER_REQUEST)
48
- sdk_info = params[:sdk] || {}
49
-
50
- return head :accepted if traces.empty?
51
-
52
- if ActiveAgent::Dashboard.multi_tenant?
53
- # Multi-tenant mode: process in background
54
- ActiveAgent::ProcessTelemetryTracesJob.perform_later(
55
- account_id: @account&.id,
56
- traces: traces.as_json,
57
- sdk_info: sdk_info.as_json,
58
- received_at: Time.current.iso8601(6)
59
- )
60
- else
61
- # Local mode: process synchronously
62
- process_traces_synchronously(traces, sdk_info)
63
- end
64
-
65
- head :accepted
66
- rescue ActionController::ParameterMissing => e
67
- render json: { error: e.message }, status: :bad_request
68
- rescue StandardError => e
69
- Rails.logger.error("[ActiveAgent::Dashboard] Trace ingestion error: #{e.message}")
70
- render json: { error: "Internal server error" }, status: :internal_server_error
71
- end
72
-
73
- private
74
-
75
- # Authenticates the request using Bearer token from Authorization header.
76
- # Only used in multi-tenant mode.
77
- def authenticate_api_key!
78
- token = extract_bearer_token
79
-
80
- if token.blank?
81
- render json: { error: "Missing Authorization header" }, status: :unauthorized
82
- return
83
- end
84
-
85
- account_class = ActiveAgent::Dashboard.account_class.constantize
86
- @account = account_class.find_by(telemetry_api_key: token)
87
-
88
- if @account.nil?
89
- render json: { error: "Invalid API key" }, status: :unauthorized
90
- return
91
- end
92
-
93
- # Track usage for rate limiting (if the account responds to it)
94
- @account.increment_telemetry_usage! if @account.respond_to?(:increment_telemetry_usage!)
95
- end
96
-
97
- # Requires the configured single-tenant ingest key when one is set.
98
- # The telemetry reporter and ruby_llm_telemetry both send their
99
- # api_key as a Bearer header, so remote apps work unchanged.
100
- def authenticate_ingest_key!
101
- expected = ActiveAgent::Dashboard.ingest_api_key
102
- return if expected.blank?
103
-
104
- token = extract_bearer_token
105
- return if token.present? && ActiveSupport::SecurityUtils.secure_compare(token, expected)
106
-
107
- render json: { error: "Invalid API key" }, status: :unauthorized
108
- end
109
-
110
- # Extracts Bearer token from Authorization header.
111
- def extract_bearer_token
112
- auth_header = request.headers["Authorization"]
113
- return nil if auth_header.blank?
114
-
115
- match = auth_header.match(/^Bearer\s+(.+)$/i)
116
- match[1] if match
117
- end
118
-
119
- # Process traces synchronously for local development.
120
- def process_traces_synchronously(traces, sdk_info)
121
- model = ActiveAgent::Dashboard.trace_model
122
-
123
- traces.each do |trace|
124
- # Skip if trace already exists (idempotency)
125
- next if model.exists?(trace_id: trace["trace_id"])
126
-
127
- model.create_from_payload(trace, sdk_info)
128
- rescue StandardError => e
129
- Rails.logger.error(
130
- "[ActiveAgent::Dashboard] Failed to process trace #{trace['trace_id']}: " \
131
- "#{e.class} - #{e.message}"
132
- )
133
- end
134
- end
135
- end
136
- end
137
- end
138
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Base controller for the ActiveAgent Dashboard.
6
- #
7
- # Handles authentication and provides helper methods for multi-tenant mode.
8
- class ApplicationController < ActionController::Base
9
- protect_from_forgery with: :exception
10
-
11
- before_action :authenticate_dashboard!
12
-
13
- # Add the engine's view path
14
- prepend_view_path ActiveAgent::Dashboard::Engine.dashboard_root.join("app", "views").to_s
15
-
16
- # Use custom layout if configured, otherwise use engine layout
17
- layout -> { ActiveAgent::Dashboard.layout || "active_agent/dashboard/application" }
18
-
19
- helper_method :current_user, :current_owner
20
-
21
- private
22
-
23
- def authenticate_dashboard!
24
- if ActiveAgent::Dashboard.authentication_method.nil?
25
- # Traces contain prompts, outputs, and error backtraces. Refuse to
26
- # serve them unauthenticated in production rather than exposing
27
- # them to the internet by default.
28
- if Rails.env.production?
29
- render plain: "ActiveAgent Dashboard: set ActiveAgent::Dashboard.authentication_method " \
30
- "(see docs/framework/dashboard.md) to enable access in production.",
31
- status: :forbidden
32
- end
33
- return
34
- end
35
-
36
- result = ActiveAgent::Dashboard.authentication_method.call(self)
37
- head :unauthorized unless result
38
- rescue StandardError => e
39
- Rails.logger.error("[ActiveAgent::Dashboard] Authentication error: #{e.message}")
40
- head :unauthorized
41
- end
42
-
43
- # Returns the current user from the host application.
44
- def current_user
45
- return nil unless ActiveAgent::Dashboard.current_user_method
46
-
47
- send(ActiveAgent::Dashboard.current_user_method)
48
- rescue NoMethodError
49
- nil
50
- end
51
-
52
- # Returns the current owner (account in multi-tenant, user otherwise).
53
- def current_owner
54
- if ActiveAgent::Dashboard.multi_tenant? && ActiveAgent::Dashboard.current_account_method
55
- send(ActiveAgent::Dashboard.current_account_method)
56
- else
57
- current_user
58
- end
59
- rescue NoMethodError
60
- nil
61
- end
62
- end
63
- end
64
- end
@@ -1,129 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Main dashboard controller showing overview metrics and recent activity.
6
- #
7
- class DashboardController < ApplicationController
8
- def index
9
- unless ActiveAgent::Dashboard.use_inertia && defined?(InertiaRails)
10
- # No ERB overview ships yet — the traces list is the dashboard.
11
- # Loading agent/run data here would also require the full
12
- # dashboard tables, which telemetry-only installs don't have.
13
- return redirect_to(traces_path)
14
- end
15
-
16
- @agents = fetch_agents.limit(10)
17
- @recent_runs = fetch_recent_runs.limit(10)
18
- @recent_traces = fetch_recent_traces.limit(10)
19
- @metrics = calculate_metrics
20
-
21
- render inertia: "Dashboard", props: {
22
- agents: serialize_agents(@agents),
23
- recentRuns: serialize_runs(@recent_runs),
24
- recentTraces: serialize_traces(@recent_traces),
25
- metrics: @metrics,
26
- user: current_user_props,
27
- account: current_account_props
28
- }
29
- end
30
-
31
- private
32
-
33
- def fetch_agents
34
- agents = Agent.order(updated_at: :desc)
35
- agents = agents.for_owner(current_owner) if current_owner
36
- agents
37
- end
38
-
39
- def fetch_recent_runs
40
- runs = AgentRun.includes(:agent).recent
41
- if current_owner && ActiveAgent::Dashboard.multi_tenant?
42
- runs = runs.joins(:agent).where(agents: { account_id: current_owner.id })
43
- end
44
- runs
45
- end
46
-
47
- def fetch_recent_traces
48
- traces = ActiveAgent::Dashboard.trace_model.recent
49
- traces = traces.for_account(current_owner) if current_owner && ActiveAgent::Dashboard.multi_tenant?
50
- traces
51
- end
52
-
53
- def calculate_metrics
54
- traces_24h = ActiveAgent::Dashboard.trace_model.where("created_at > ?", 24.hours.ago)
55
- runs_24h = AgentRun.where("created_at > ?", 24.hours.ago)
56
-
57
- if current_owner && ActiveAgent::Dashboard.multi_tenant?
58
- traces_24h = traces_24h.for_account(current_owner)
59
- runs_24h = runs_24h.joins(:agent).where(agents: { account_id: current_owner.id })
60
- end
61
-
62
- {
63
- total_agents: fetch_agents.count,
64
- active_agents: fetch_agents.active_agents.count,
65
- total_runs_24h: runs_24h.count,
66
- successful_runs_24h: runs_24h.successful.count,
67
- failed_runs_24h: runs_24h.failed_runs.count,
68
- total_traces_24h: traces_24h.count,
69
- total_tokens_24h: traces_24h.sum(:total_input_tokens).to_i + traces_24h.sum(:total_output_tokens).to_i,
70
- avg_duration_ms: runs_24h.where.not(duration_ms: nil).average(:duration_ms)&.round || 0
71
- }
72
- end
73
-
74
- def serialize_agents(agents)
75
- agents.map do |agent|
76
- {
77
- id: agent.id,
78
- name: agent.name,
79
- slug: agent.slug,
80
- description: agent.description,
81
- provider: agent.provider,
82
- model: agent.model,
83
- status: agent.status,
84
- presetType: agent.preset_type,
85
- versionCount: agent.version_count,
86
- updatedAt: agent.updated_at.iso8601
87
- }
88
- end
89
- end
90
-
91
- def serialize_runs(runs)
92
- runs.map(&:summary)
93
- end
94
-
95
- def serialize_traces(traces)
96
- traces.map do |trace|
97
- {
98
- id: trace.id,
99
- traceId: trace.trace_id,
100
- displayName: trace.display_name,
101
- status: trace.status,
102
- duration: trace.formatted_duration,
103
- tokens: trace.formatted_tokens,
104
- timestamp: trace.timestamp&.iso8601
105
- }
106
- end
107
- end
108
-
109
- def current_user_props
110
- return nil unless current_user
111
-
112
- {
113
- id: current_user.id,
114
- name: current_user.try(:display_name) || current_user.try(:name) || current_user.try(:email),
115
- email: current_user.try(:email)
116
- }
117
- end
118
-
119
- def current_account_props
120
- return nil unless current_owner && ActiveAgent::Dashboard.multi_tenant?
121
-
122
- {
123
- id: current_owner.id,
124
- name: current_owner.try(:name)
125
- }
126
- end
127
- end
128
- end
129
- end
@@ -1,123 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Controller for viewing telemetry traces.
6
- #
7
- # Provides:
8
- # - List view with filtering and pagination
9
- # - Detail view with span timeline
10
- # - Metrics overview
11
- # - Live updates via Turbo Streams
12
- class TracesController < ApplicationController
13
- def index
14
- @traces = fetch_traces
15
- @metrics = calculate_metrics
16
-
17
- respond_to do |format|
18
- format.html
19
- # turbo-rails is optional; responding to an unregistered MIME
20
- # type raises in host apps without it.
21
- format.turbo_stream if turbo_stream_available?
22
- end
23
- end
24
-
25
- def show
26
- @trace = scoped_traces.find(params[:id])
27
- end
28
-
29
- def metrics
30
- @metrics = calculate_metrics
31
- @agent_stats = agent_statistics
32
- @time_series = time_series_data
33
-
34
- respond_to do |format|
35
- format.html
36
- format.turbo_stream if turbo_stream_available?
37
- end
38
- end
39
-
40
- private
41
-
42
- def turbo_stream_available?
43
- Mime::Type.lookup_by_extension(:turbo_stream).present?
44
- end
45
-
46
- # All queries honor the configured trace model and, in multi-tenant
47
- # mode, the current owner's account (for_account no-ops otherwise).
48
- #
49
- # In multi-tenant mode an unresolvable owner returns nothing rather
50
- # than falling through: current_owner degrades to current_user when
51
- # current_account_method is unset, which would scope by a user id
52
- # against account_id — another tenant's traces.
53
- def scoped_traces
54
- model = ActiveAgent::Dashboard.trace_model
55
- return model.none if ActiveAgent::Dashboard.multi_tenant? && current_owner.nil?
56
-
57
- model.for_account(current_owner)
58
- end
59
-
60
- def fetch_traces
61
- traces = scoped_traces.recent
62
-
63
- traces = traces.for_agent(params[:agent]) if params[:agent].present?
64
- traces = traces.with_errors if params[:status] == "error"
65
- traces = traces.for_service(params[:service]) if params[:service].present?
66
-
67
- if params[:start_date].present? && params[:end_date].present?
68
- traces = traces.for_date_range(
69
- Time.parse(params[:start_date]),
70
- Time.parse(params[:end_date])
71
- )
72
- end
73
-
74
- traces.limit(params[:limit] || 50)
75
- end
76
-
77
- def calculate_metrics
78
- traces = scoped_traces.where(
79
- "created_at > ?", 24.hours.ago
80
- )
81
-
82
- {
83
- total_traces: traces.count,
84
- total_tokens: traces.sum(:total_input_tokens) + traces.sum(:total_output_tokens),
85
- avg_duration_ms: traces.average(:total_duration_ms)&.round(2) || 0,
86
- error_rate: calculate_error_rate(traces),
87
- unique_agents: traces.distinct.count(:agent_class)
88
- }
89
- end
90
-
91
- def calculate_error_rate(traces)
92
- total = traces.count
93
- return 0.0 if total.zero?
94
-
95
- errors = traces.with_errors.count
96
- ((errors.to_f / total) * 100).round(2)
97
- end
98
-
99
- def agent_statistics
100
- scoped_traces
101
- .where("created_at > ?", 24.hours.ago)
102
- .group(:agent_class)
103
- .select(
104
- "agent_class",
105
- "COUNT(*) as trace_count",
106
- "SUM(total_input_tokens + total_output_tokens) as total_tokens",
107
- "AVG(total_duration_ms) as avg_duration",
108
- "SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END) as error_count"
109
- )
110
- end
111
-
112
- def time_series_data
113
- scoped_traces
114
- .where("created_at > ?", 1.hour.ago)
115
- .group_by_minute(:created_at)
116
- .count
117
- rescue NoMethodError
118
- # Fallback if groupdate gem not available
119
- {}
120
- end
121
- end
122
- end
123
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Background job for executing agent runs.
6
- #
7
- # Handles the actual execution of an agent with the given input,
8
- # updating the AgentRun record with results.
9
- #
10
- class AgentExecutionJob < ApplicationJob
11
- queue_as :default
12
-
13
- def perform(agent_run_id)
14
- run = AgentRun.find(agent_run_id)
15
- return if run.finished?
16
-
17
- run.update!(status: :running, started_at: Time.current)
18
-
19
- begin
20
- agent = run.agent
21
- result = execute_agent(agent, run.input_prompt, **(run.input_params || {}))
22
-
23
- run.update!(
24
- output: result[:output],
25
- output_metadata: result[:metadata],
26
- status: :complete,
27
- completed_at: Time.current,
28
- duration_ms: ((Time.current - run.started_at) * 1000).to_i,
29
- input_tokens: result.dig(:usage, :input_tokens),
30
- output_tokens: result.dig(:usage, :output_tokens),
31
- total_tokens: result.dig(:usage, :total_tokens)
32
- )
33
- rescue => e
34
- run.update!(
35
- status: :failed,
36
- completed_at: Time.current,
37
- error_message: e.message,
38
- error_backtrace: e.backtrace&.first(10)&.join("\n")
39
- )
40
- end
41
- end
42
-
43
- private
44
-
45
- def execute_agent(agent, input_prompt, **params)
46
- # TODO: Build and execute actual ActiveAgent::Base subclass
47
- # For now, return mock data
48
- {
49
- output: "Executed: #{input_prompt}",
50
- metadata: { provider: agent.provider, model: agent.model },
51
- usage: { input_tokens: 100, output_tokens: 200, total_tokens: 300 }
52
- }
53
- end
54
- end
55
- end
56
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Base class for all Dashboard engine jobs.
6
- class ApplicationJob < ActiveJob::Base
7
- # Retry failed jobs
8
- retry_on StandardError, wait: :polynomially_longer, attempts: 3
9
-
10
- # Discard jobs for records that no longer exist
11
- discard_on ActiveRecord::RecordNotFound
12
- end
13
- end
14
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveAgent
4
- module Dashboard
5
- # Background job for cleaning up sandbox environments.
6
- #
7
- # Removes containers, Cloud Run jobs, or other resources
8
- # when a sandbox session expires.
9
- #
10
- class SandboxCleanupJob < ApplicationJob
11
- queue_as :default
12
-
13
- def perform(sandbox_session_id)
14
- session = SandboxSession.find_by(id: sandbox_session_id)
15
- return unless session
16
-
17
- cleanup_sandbox(session)
18
- end
19
-
20
- private
21
-
22
- def cleanup_sandbox(session)
23
- case ActiveAgent::Dashboard.sandbox_service
24
- when :cloud_run
25
- cleanup_cloud_run(session)
26
- when :kubernetes
27
- cleanup_kubernetes(session)
28
- else
29
- cleanup_local(session)
30
- end
31
- end
32
-
33
- def cleanup_local(session)
34
- # Local mode: Nothing to clean up
35
- Rails.logger.info "[ActiveAgent::Dashboard] Cleaned up local sandbox: #{session.session_id}"
36
- end
37
-
38
- def cleanup_cloud_run(session)
39
- # TODO: Implement Cloud Run cleanup
40
- Rails.logger.info "[ActiveAgent::Dashboard] Would clean up Cloud Run job: #{session.cloud_run_job_id}"
41
- end
42
-
43
- def cleanup_kubernetes(session)
44
- # TODO: Implement Kubernetes cleanup
45
- Rails.logger.info "[ActiveAgent::Dashboard] Would clean up Kubernetes pod: #{session.cloud_run_job_id}"
46
- end
47
- end
48
- end
49
- end