actionagent 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/builds/action_agent.css +1 -1
- data/app/assets/builds/action_agent.js +76 -73
- data/app/assets/builds/action_agent_recorder.js +78 -0
- data/app/assets/builds/action_agent_replay.js +226 -0
- data/app/controllers/action_agent/api/agent_runs_controller.rb +3 -1
- data/app/controllers/action_agent/api/agents_controller.rb +1 -0
- data/app/controllers/action_agent/api/api_keys_controller.rb +12 -2
- data/app/controllers/action_agent/api/base_controller.rb +63 -2
- data/app/controllers/action_agent/api/claude_logins_controller.rb +93 -0
- data/app/controllers/action_agent/api/code_sessions_controller.rb +71 -12
- data/app/controllers/action_agent/api/dashboard_assistant_controller.rb +2 -2
- data/app/controllers/action_agent/api/draft_pull_requests_controller.rb +248 -0
- data/app/controllers/action_agent/api/evaluation_fixes_controller.rb +27 -0
- data/app/controllers/action_agent/api/evaluations_controller.rb +58 -7
- data/app/controllers/action_agent/api/explorations_controller.rb +213 -0
- data/app/controllers/action_agent/api/github_app_manifests_controller.rb +125 -0
- data/app/controllers/action_agent/api/github_connections_controller.rb +40 -2
- data/app/controllers/action_agent/api/github_installations_controller.rb +280 -0
- data/app/controllers/action_agent/api/input_requests_controller.rb +77 -0
- data/app/controllers/action_agent/api/mcp_controller.rb +46 -12
- data/app/controllers/action_agent/api/members_controller.rb +36 -0
- data/app/controllers/action_agent/api/project_explorations_controller.rb +80 -0
- data/app/controllers/action_agent/api/project_install_pull_requests_controller.rb +305 -0
- data/app/controllers/action_agent/api/project_secrets_controller.rb +96 -0
- data/app/controllers/action_agent/api/project_setup_controller.rb +151 -0
- data/app/controllers/action_agent/api/project_sign_ins_controller.rb +112 -0
- data/app/controllers/action_agent/api/projects_controller.rb +512 -0
- data/app/controllers/action_agent/api/provider_keys_controller.rb +176 -24
- data/app/controllers/action_agent/api/provider_models_controller.rb +37 -15
- data/app/controllers/action_agent/api/recording_event_ingest_controller.rb +67 -0
- data/app/controllers/action_agent/api/sandbox_browser_tickets_controller.rb +50 -0
- data/app/controllers/action_agent/api/sandbox_browsers_controller.rb +65 -0
- data/app/controllers/action_agent/api/sandboxes_controller.rb +148 -8
- data/app/controllers/action_agent/api/scenario_catalog_entries_controller.rb +188 -0
- data/app/controllers/action_agent/api/scenario_catalogs_controller.rb +302 -0
- data/app/controllers/action_agent/api/session_recordings_controller.rb +165 -34
- data/app/controllers/action_agent/api/sessions_controller.rb +111 -0
- data/app/controllers/action_agent/api/telemetry_keys_controller.rb +21 -0
- data/app/controllers/action_agent/dashboard_controller.rb +17 -6
- data/app/controllers/action_agent/session_player_controller.rb +58 -0
- data/app/controllers/concerns/action_agent/api/agent_serialization.rb +1 -0
- data/app/controllers/concerns/action_agent/api/mcp_dashboard_tools.rb +627 -20
- data/app/controllers/concerns/action_agent/api/project_browser.rb +46 -0
- data/app/controllers/concerns/action_agent/api/project_secret_authorization.rb +57 -0
- data/app/jobs/action_agent/agent_execution_job.rb +9 -39
- data/app/jobs/action_agent/agent_resume_job.rb +84 -0
- data/app/jobs/action_agent/code_session_job.rb +39 -10
- data/app/jobs/action_agent/draft_pull_request_job.rb +43 -0
- data/app/jobs/action_agent/exploration_job.rb +80 -0
- data/app/jobs/action_agent/input_request_expiry_job.rb +22 -0
- data/app/jobs/action_agent/project_evaluation_job.rb +59 -0
- data/app/jobs/action_agent/project_setup_job.rb +21 -0
- data/app/jobs/action_agent/sandbox_cleanup_job.rb +20 -3
- data/app/jobs/action_agent/sandbox_provision_job.rb +77 -10
- data/app/jobs/action_agent/sandbox_run_job.rb +9 -37
- data/app/jobs/action_agent/verify_evaluation_fix_job.rb +37 -0
- data/app/models/action_agent/agent.rb +72 -26
- data/app/models/action_agent/agent_run.rb +134 -23
- data/app/models/action_agent/agent_template.rb +35 -1
- data/app/models/action_agent/api_key.rb +14 -0
- data/app/models/action_agent/catalog_scenario.rb +75 -0
- data/app/models/action_agent/code_session.rb +21 -5
- data/app/models/action_agent/draft_pull_request.rb +131 -0
- data/app/models/action_agent/evaluation.rb +148 -0
- data/app/models/action_agent/evaluation_run.rb +143 -53
- data/app/models/action_agent/evaluation_scenario_result.rb +15 -3
- data/app/models/action_agent/exploration.rb +847 -0
- data/app/models/action_agent/github_installation.rb +159 -0
- data/app/models/action_agent/input_request.rb +291 -0
- data/app/models/action_agent/model_pricing.rb +214 -34
- data/app/models/action_agent/project.rb +789 -0
- data/app/models/action_agent/project_secret.rb +339 -0
- data/app/models/action_agent/provider_key.rb +207 -4
- data/app/models/action_agent/recording_event.rb +145 -0
- data/app/models/action_agent/recording_snapshot.rb +7 -7
- data/app/models/action_agent/sandbox_session.rb +300 -16
- data/app/models/action_agent/scenario_catalog.rb +172 -0
- data/app/models/action_agent/scenario_product.rb +57 -0
- data/app/models/action_agent/scenario_set.rb +138 -0
- data/app/models/action_agent/session_recording.rb +121 -14
- data/app/models/action_agent/telemetry_trace.rb +74 -19
- data/app/models/concerns/action_agent/ownable.rb +84 -13
- data/app/serializers/action_agent/evaluation_serializer.rb +41 -10
- data/app/serializers/action_agent/input_request_serializer.rb +47 -0
- data/app/serializers/action_agent/scenario_catalog_serializer.rb +23 -0
- data/app/serializers/action_agent/session_summary_serializer.rb +146 -0
- data/app/services/action_agent/agent_execution_service.rb +450 -72
- data/app/services/action_agent/agent_scorecard.rb +50 -21
- data/app/services/action_agent/agent_sync.rb +5 -2
- data/app/services/action_agent/agent_tool_roster.rb +19 -4
- data/app/services/action_agent/agent_toolbox.rb +215 -9
- data/app/services/action_agent/browser_live_ticket.rb +78 -0
- data/app/services/action_agent/browser_sidecar.rb +213 -0
- data/app/services/action_agent/browser_sign_in.rb +163 -0
- data/app/services/action_agent/browser_tool_redaction.rb +92 -0
- data/app/services/action_agent/claude_code_auth.rb +70 -4
- data/app/services/action_agent/codex_session_events.rb +37 -0
- data/app/services/action_agent/dashboard_assistant_service.rb +24 -10
- data/app/services/action_agent/draft_pull_request_publisher.rb +719 -0
- data/app/services/action_agent/evaluation_fix.rb +101 -0
- data/app/services/action_agent/evaluation_fix_comparison.rb +66 -0
- data/app/services/action_agent/evaluation_report_import.rb +84 -5
- data/app/services/action_agent/evaluation_run_cost.rb +609 -0
- data/app/services/action_agent/evaluation_runner_service.rb +29 -10
- data/app/services/action_agent/evaluation_standing.rb +152 -0
- data/app/services/action_agent/explorer_execution_service.rb +517 -0
- data/app/services/action_agent/github_client.rb +405 -64
- data/app/services/action_agent/live_updates.rb +31 -0
- data/app/services/action_agent/local_sandbox_backend.rb +1263 -102
- data/app/services/action_agent/local_sandbox_claude_login.rb +151 -0
- data/app/services/action_agent/mcp_client.rb +6 -2
- data/app/services/action_agent/mcp_recording_middleware.rb +104 -145
- data/app/services/action_agent/mcp_tool_dispatcher.rb +36 -13
- data/app/services/action_agent/mock_sandbox_backend.rb +3 -1
- data/app/services/action_agent/playwright_mcp_client.rb +7 -0
- data/app/services/action_agent/project_boot_fix.rb +309 -0
- data/app/services/action_agent/project_capabilities.rb +200 -0
- data/app/services/action_agent/project_install_pull_request.rb +225 -0
- data/app/services/action_agent/project_preflight.rb +178 -0
- data/app/services/action_agent/project_secret_discovery.rb +142 -0
- data/app/services/action_agent/project_setup.rb +429 -0
- data/app/services/action_agent/provider_credentials.rb +102 -0
- data/app/services/action_agent/recording_event_ingest.rb +181 -0
- data/app/services/action_agent/runtime_tool_roster.rb +52 -0
- data/app/services/action_agent/sample_data/conference_ticket.rb +516 -0
- data/app/services/action_agent/sandbox_boot_spec.rb +558 -0
- data/app/services/action_agent/sandbox_browser.rb +299 -0
- data/app/services/action_agent/sandbox_browser_driver.rb +85 -0
- data/app/services/action_agent/sandbox_credential_paths.rb +26 -0
- data/app/services/action_agent/sandbox_mail.rb +103 -0
- data/app/services/action_agent/sandbox_manifest.rb +166 -13
- data/app/services/action_agent/sandbox_orchestrator.rb +290 -23
- data/app/services/action_agent/sandbox_patch.rb +253 -0
- data/app/services/action_agent/scenario_catalog_import.rb +255 -0
- data/app/services/action_agent/scenario_catalog_repository_import.rb +81 -0
- data/app/services/action_agent/scenario_evaluation_runner.rb +121 -17
- data/app/services/action_agent/secret_scrubber.rb +33 -4
- data/app/services/action_agent/session_index.rb +270 -0
- data/app/services/action_agent/session_recording_service.rb +11 -0
- data/app/services/action_agent/session_timeline.rb +505 -0
- data/app/views/action_agent/github_app_manifests/created.html.erb +55 -0
- data/app/views/action_agent/session_player/show.html.erb +19 -0
- data/config/routes.rb +153 -2
- data/lib/action_agent/claude_login_process.rb +107 -0
- data/lib/action_agent/engine.rb +55 -5
- data/lib/action_agent/secret_requests.rb +80 -0
- data/lib/action_agent/version.rb +1 -1
- data/lib/action_agent.rb +513 -22
- data/lib/generators/action_agent/install_generator.rb +45 -5
- data/lib/generators/action_agent/templates/action_agent.rb.erb +161 -0
- data/lib/generators/action_agent/templates/add_code_session_runner.rb.erb +9 -0
- data/lib/generators/action_agent/templates/migrations/015_add_provider_key_scope.rb.erb +72 -0
- data/lib/generators/action_agent/templates/migrations/016_create_active_agent_input_requests.rb.erb +85 -0
- data/lib/generators/action_agent/templates/migrations/017_create_active_agent_recording_events.rb.erb +57 -0
- data/lib/generators/action_agent/templates/migrations/018_add_browser_to_active_agent_sandbox_sessions.rb.erb +25 -0
- data/lib/generators/action_agent/templates/migrations/019_create_active_agent_projects.rb.erb +79 -0
- data/lib/generators/action_agent/templates/migrations/020_add_github_app_installations.rb.erb +62 -0
- data/lib/generators/action_agent/templates/migrations/021_create_active_agent_explorations.rb.erb +63 -0
- data/lib/generators/action_agent/templates/migrations/023_create_active_agent_draft_pull_requests.rb.erb +75 -0
- data/lib/generators/action_agent/templates/migrations/024_add_kind_to_active_agent_project_secrets.rb.erb +14 -0
- data/lib/generators/action_agent/templates/migrations/025_create_active_agent_scenario_catalogs.rb.erb +110 -0
- data/lib/generators/action_agent/templates/migrations/026_add_evaluation_fixes_to_code_sessions.rb.erb +21 -0
- data/lib/tasks/action_agent.rake +33 -0
- data/lib/tasks/browser.rake +22 -0
- data/lib/tasks/sandbox.rake +1 -1
- data/lib/tasks/scenario_catalogs.rake +71 -0
- metadata +100 -2
|
@@ -190,7 +190,9 @@ module ActionAgent
|
|
|
190
190
|
# The cost here is the agent's: what the sampled interactions cost to
|
|
191
191
|
# operate. It is not what this run spent — scoring recorded generations
|
|
192
192
|
# costs nothing until a judge is asked, and the judge's own spend is
|
|
193
|
-
# recorded apart, under "_judge_usage" (see #judge_usage).
|
|
193
|
+
# recorded apart, under "_judge_usage" (see #judge_usage). It sums the
|
|
194
|
+
# generations that could be priced, and "priced" counts them, so a
|
|
195
|
+
# cohort with some unpriced generations reads as partial.
|
|
194
196
|
|
|
195
197
|
def cohort_summaries(samples, per_sample_scores)
|
|
196
198
|
samples
|
|
@@ -207,7 +209,8 @@ module ActionAgent
|
|
|
207
209
|
input_tokens = samples.sum { |generation| generation.input_tokens.to_i }
|
|
208
210
|
output_tokens = samples.sum { |generation| generation.output_tokens.to_i }
|
|
209
211
|
costs = samples.filter_map do |generation|
|
|
210
|
-
ModelPricing.estimate(model: generation.model,
|
|
212
|
+
ModelPricing.estimate(model: generation.model, provider: generation.provider.presence, input_tokens: generation.input_tokens,
|
|
213
|
+
output_tokens: generation.output_tokens)
|
|
211
214
|
end
|
|
212
215
|
|
|
213
216
|
{
|
|
@@ -217,7 +220,8 @@ module ActionAgent
|
|
|
217
220
|
"avg_duration_ms" => durations_ms.any? ? (durations_ms.sum / durations_ms.size).round : nil,
|
|
218
221
|
"input_tokens" => input_tokens,
|
|
219
222
|
"output_tokens" => output_tokens,
|
|
220
|
-
"cost" => costs.any? ? costs.sum.round(6) : nil
|
|
223
|
+
"cost" => costs.any? ? costs.sum.round(6) : nil,
|
|
224
|
+
"priced" => costs.size
|
|
221
225
|
}
|
|
222
226
|
end
|
|
223
227
|
|
|
@@ -254,10 +258,10 @@ module ActionAgent
|
|
|
254
258
|
|
|
255
259
|
def record_judge_call(kind, response)
|
|
256
260
|
usage = response.respond_to?(:usage) ? response.usage : nil
|
|
257
|
-
input_tokens = usage
|
|
261
|
+
input_tokens = judge_input_tokens(usage)
|
|
258
262
|
output_tokens = usage.respond_to?(:output_tokens) ? usage.output_tokens.to_i : 0
|
|
259
263
|
model = (response.respond_to?(:model) && response.model.presence) || @evaluation.judge_model.presence
|
|
260
|
-
cost = ModelPricing.estimate(model: model, input_tokens: input_tokens, output_tokens: output_tokens)
|
|
264
|
+
cost = ModelPricing.estimate(model: model, provider: judge_provider, input_tokens: input_tokens, output_tokens: output_tokens)
|
|
261
265
|
|
|
262
266
|
@judge_usage ||= { "calls" => 0, "input_tokens" => 0, "output_tokens" => 0, "cost" => nil, "model" => nil, "by_kind" => {} }
|
|
263
267
|
@judge_usage["calls"] += 1
|
|
@@ -268,6 +272,22 @@ module ActionAgent
|
|
|
268
272
|
@judge_usage["by_kind"][kind.to_s] = @judge_usage["by_kind"].fetch(kind.to_s, 0) + 1
|
|
269
273
|
end
|
|
270
274
|
|
|
275
|
+
# Every token the judge was billed for reading. Anthropic reports the
|
|
276
|
+
# tokens read from or written to the prompt cache apart from
|
|
277
|
+
# `input_tokens`, so they are added; OpenAI's prompt count already holds
|
|
278
|
+
# its cached tokens. Priced at the input rate, which overstates a cache
|
|
279
|
+
# read: the figure is an upper bound.
|
|
280
|
+
def judge_input_tokens(usage)
|
|
281
|
+
return 0 unless usage.respond_to?(:input_tokens)
|
|
282
|
+
|
|
283
|
+
total = usage.input_tokens.to_i
|
|
284
|
+
return total unless judge_provider == :anthropic
|
|
285
|
+
|
|
286
|
+
total += usage.cached_tokens.to_i if usage.respond_to?(:cached_tokens)
|
|
287
|
+
total += usage.cache_creation_tokens.to_i if usage.respond_to?(:cache_creation_tokens)
|
|
288
|
+
total
|
|
289
|
+
end
|
|
290
|
+
|
|
271
291
|
def sample_generations(model: nil)
|
|
272
292
|
scope = @evaluation.agent.generations
|
|
273
293
|
scope = scope.where(model: model) if model
|
|
@@ -574,13 +594,12 @@ module ActionAgent
|
|
|
574
594
|
|
|
575
595
|
# The evaluated agent owner's credential for +name+ (Settings ->
|
|
576
596
|
# Provider API Keys, or whatever the host app resolves); preferred over
|
|
577
|
-
# the host's config/active_agent.yml credentials for the judge.
|
|
597
|
+
# the host's config/active_agent.yml credentials for the judge. Resolved
|
|
598
|
+
# with no actor: the judge scores for the organization, so it never runs
|
|
599
|
+
# on a member's personal key.
|
|
578
600
|
def owner_provider_options(name)
|
|
579
601
|
@owner_provider_options ||= {}
|
|
580
|
-
@owner_provider_options[name.to_s] ||=
|
|
581
|
-
from_host = ActionAgent.provider_credentials(owner, name.to_s)
|
|
582
|
-
from_host.presence || ProviderKey.for_owner(owner).find_by(provider: name.to_s)&.generation_options || {}
|
|
583
|
-
end
|
|
602
|
+
@owner_provider_options[name.to_s] ||= ProviderCredentials.resolve(owner: owner, actor: nil, provider: name).options
|
|
584
603
|
end
|
|
585
604
|
|
|
586
605
|
def owner
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAgent
|
|
4
|
+
# Where an evaluation stands against the agent as it is now, so a pass
|
|
5
|
+
# rate on the Evaluations page describes the current agent rather than
|
|
6
|
+
# pooling every suite's last run, however old the code it scored.
|
|
7
|
+
#
|
|
8
|
+
# The evaluation's headline run is its newest complete run; a newer run
|
|
9
|
+
# still pending or failed shows beside it, never in its place. Its
|
|
10
|
+
# standing is one of:
|
|
11
|
+
#
|
|
12
|
+
# current — the headline run scored the agent's current version
|
|
13
|
+
# stale — it scored an earlier version, or a release the dashboard
|
|
14
|
+
# has not matched to the agent's current release
|
|
15
|
+
# unrecorded — the run recorded no version and the agent has no release
|
|
16
|
+
# versions at all, so nothing says which code it scored
|
|
17
|
+
# archived — the evaluation is archived (Evaluation#archive!)
|
|
18
|
+
# none — no complete run
|
|
19
|
+
#
|
|
20
|
+
# Only edits that change how a run goes count as a new version: what the
|
|
21
|
+
# model is given (the instructions, the action prompts, the tools, the MCP
|
|
22
|
+
# servers, the model config and the response format), and the tools whose
|
|
23
|
+
# calls wait for a person's approval, which pause a replay. A dashboard
|
|
24
|
+
# edit to the agent's appearance cuts an AgentVersion like any other edit,
|
|
25
|
+
# but the run before it scored the same agent and stays current.
|
|
26
|
+
class EvaluationStanding
|
|
27
|
+
STANDINGS = %w[current stale unrecorded archived none].freeze
|
|
28
|
+
VERSION_STATES = %w[current earlier unrecorded].freeze
|
|
29
|
+
# The configuration snapshot keys the model is given.
|
|
30
|
+
MODEL_FACING = %w[instructions action_prompts tools mcp_servers model_config response_format].freeze
|
|
31
|
+
# The configuration snapshot keys that change how a run goes.
|
|
32
|
+
SCORED = (MODEL_FACING + %w[approval_required_tools]).freeze
|
|
33
|
+
|
|
34
|
+
# Gives every evaluation its standing with one query per table rather
|
|
35
|
+
# than one per evaluation: the agents' latest versions and whether each
|
|
36
|
+
# has a release are read for the page at once.
|
|
37
|
+
def self.preload(evaluations)
|
|
38
|
+
evaluations = Array(evaluations)
|
|
39
|
+
agent_ids = evaluations.filter_map(&:agent_id).uniq
|
|
40
|
+
return if agent_ids.empty?
|
|
41
|
+
|
|
42
|
+
newest = AgentVersion.where(agent_id: agent_ids).group(:agent_id).maximum(:version_number)
|
|
43
|
+
latest = AgentVersion.where(agent_id: agent_ids).where(version_number: newest.values.uniq)
|
|
44
|
+
.select { |version| newest[version.agent_id] == version.version_number }.index_by(&:agent_id)
|
|
45
|
+
releases = AgentVersion.releases.where(agent_id: agent_ids).distinct.pluck(:agent_id).to_set
|
|
46
|
+
|
|
47
|
+
evaluations.each do |evaluation|
|
|
48
|
+
evaluation.standing_info = new(evaluation, latest_version: latest[evaluation.agent_id], has_releases: releases.include?(evaluation.agent_id))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
attr_reader :evaluation
|
|
53
|
+
|
|
54
|
+
# @param headline_run [EvaluationRun, nil] the headline run when the
|
|
55
|
+
# caller already holds it (AgentScorecard selects one per evaluation)
|
|
56
|
+
def initialize(evaluation, latest_version: :unknown, has_releases: :unknown, headline_run: :unknown)
|
|
57
|
+
@evaluation = evaluation
|
|
58
|
+
@latest_version = latest_version
|
|
59
|
+
@has_releases = has_releases
|
|
60
|
+
@headline_run = headline_run
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def headline_run
|
|
64
|
+
return @headline_run unless @headline_run == :unknown
|
|
65
|
+
|
|
66
|
+
@headline_run = evaluation.headline_run
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The same standing read against +run+ as the headline run.
|
|
70
|
+
def with_headline(run)
|
|
71
|
+
self.class.new(evaluation, latest_version: latest_version, has_releases: has_releases?, headline_run: run)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def standing
|
|
75
|
+
return "archived" if evaluation.archived?
|
|
76
|
+
return "none" unless headline_run
|
|
77
|
+
|
|
78
|
+
case version_state(headline_run)
|
|
79
|
+
when "current" then "current"
|
|
80
|
+
when "unrecorded" then has_releases? ? "stale" : "unrecorded"
|
|
81
|
+
else "stale"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Whether +run+ scored the agent's current version: "current",
|
|
86
|
+
# "earlier", or "unrecorded" when it recorded no version.
|
|
87
|
+
def version_state(run)
|
|
88
|
+
version = run&.agent_version
|
|
89
|
+
return "unrecorded" unless version
|
|
90
|
+
|
|
91
|
+
# The agent's last recorded deploy is the truth about the code that
|
|
92
|
+
# runs: a release version matching it is current whatever the
|
|
93
|
+
# dashboard edited since, and one that does not is earlier even when
|
|
94
|
+
# it was recorded last (a report of an older deploy published late).
|
|
95
|
+
if version.release? && (deployed = evaluation.agent&.release_digest.presence)
|
|
96
|
+
return deployed == version.release_digest ? "current" : "earlier"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
latest = latest_version
|
|
100
|
+
return "current" if latest.nil? || latest.id == version.id
|
|
101
|
+
return "current" if scored_configuration(version) == scored_configuration(latest)
|
|
102
|
+
|
|
103
|
+
"earlier"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Passes per model label of the headline run, `{ label => { passed, total } }`,
|
|
107
|
+
# from its recorded summaries; empty without them.
|
|
108
|
+
def per_model
|
|
109
|
+
run = headline_run
|
|
110
|
+
return {} unless run
|
|
111
|
+
|
|
112
|
+
summaries = run.scores.is_a?(Hash) ? (run.scores["_models"].presence || run.scores["_cohorts"]) : nil
|
|
113
|
+
return {} unless summaries.is_a?(Hash)
|
|
114
|
+
|
|
115
|
+
summaries.filter_map do |label, stats|
|
|
116
|
+
next unless stats.is_a?(Hash)
|
|
117
|
+
|
|
118
|
+
total = (stats["scenarios"] || stats["samples"]).to_i
|
|
119
|
+
[ label, { passed: stats["passed"].to_i, total: total } ]
|
|
120
|
+
end.to_h
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# What the index and the scorecards pool: a current or unrecorded
|
|
124
|
+
# headline run; a stale or archived evaluation is left out and said so.
|
|
125
|
+
def counted?
|
|
126
|
+
%w[current unrecorded].include?(standing)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def latest_version
|
|
132
|
+
return @latest_version unless @latest_version == :unknown
|
|
133
|
+
|
|
134
|
+
@latest_version = evaluation.agent&.latest_version
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def has_releases?
|
|
138
|
+
return @has_releases unless @has_releases == :unknown
|
|
139
|
+
|
|
140
|
+
@has_releases = evaluation.agent&.agent_versions&.releases&.exists? || false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# A snapshot taken before an agent had an approval list reads as an empty
|
|
144
|
+
# one, so the list's arrival alone cuts no new version.
|
|
145
|
+
def scored_configuration(version)
|
|
146
|
+
snapshot = version.configuration_snapshot.to_h.stringify_keys
|
|
147
|
+
configuration = SCORED.to_h { |key| [ key, snapshot[key] ] }
|
|
148
|
+
configuration["approval_required_tools"] = Array(configuration["approval_required_tools"])
|
|
149
|
+
configuration
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|