Class: ActionAgent::AgentRun
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- ActionAgent::AgentRun
- Defined in:
- app/models/action_agent/agent_run.rb
Constant Summary collapse
- CODENAME_ADJECTIVES =
Deterministic memorable name for the digest ("calm-heron") — reads far better than hex when comparing cohorts, and is stable across runs and deployments because it's derived from the digest alone.
%w[ calm brisk quiet bold amber coral dusky fresh golden keen lively mellow nimble pale rustic silver tidal vivid wry zesty arid breezy crisp dapper eager foggy hazy icy jolly lunar misty polar ].freeze
- CODENAME_NOUNS =
%w[ heron otter falcon cedar willow harbor mesa ridge grove delta prairie summit canyon reef atoll fjord tundra oasis lagoon dune glacier meadow bluff cove marsh basin knoll strait quarry vale hollow crag ].freeze
Instance Method Summary collapse
-
#add_log(message, level: :info) ⇒ Object
Add a log entry.
-
#append_event(eid:, kind:, label:, status: "done", detail: nil, duration_ms: nil) ⇒ Object
Appends a progress event to logs mid-run so pollers can stream what the agent is doing (pending llm/tool/agent calls).
-
#broadcast_update ⇒ Object
Stream output updates via ActionCable.
-
#calculated_duration_ms ⇒ Object
Calculate duration if not set.
-
#cancel! ⇒ Object
Cancel a running execution.
-
#finished? ⇒ Boolean
Check if run is finished.
-
#in_progress? ⇒ Boolean
Check if run is still in progress.
- #instructions_codename ⇒ Object
-
#instructions_digest ⇒ Object
Stable short fingerprint of the instructions this run executed under — the grouping key (with model) for configuration cohorts when comparing instruction/model changes.
-
#summary ⇒ Object
Get a summary for display.
Methods inherited from ApplicationRecord
Instance Method Details
#add_log(message, level: :info) ⇒ Object
Add a log entry
24 25 26 27 28 29 30 31 32 |
# File 'app/models/action_agent/agent_run.rb', line 24 def add_log(, level: :info) new_logs = logs || [] new_logs << { timestamp: Time.current.iso8601, level: level.to_s, message: } update!(logs: new_logs) end |
#append_event(eid:, kind:, label:, status: "done", detail: nil, duration_ms: nil) ⇒ Object
Appends a progress event to logs mid-run so pollers can stream what the agent is doing (pending llm/tool/agent calls). Events pair up by eid: a "started" event is pending until a "done"/"error" with the same eid lands. update_column: no validations/callbacks, safe from the run's own execution thread; reads current DB state so add_log interleaves safely.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'app/models/action_agent/agent_run.rb', line 39 def append_event(eid:, kind:, label:, status: "done", detail: nil, duration_ms: nil) event = { "at" => Time.current.iso8601(3), "eid" => eid, "kind" => kind.to_s, "label" => label.to_s, "status" => status.to_s } event["detail"] = detail.to_s.byteslice(0, 1200).to_s.scrub if detail event["duration_ms"] = duration_ms if duration_ms current = self.class.where(id: id).pick(:logs) || [] update_column(:logs, current + [ event ]) event end |
#broadcast_update ⇒ Object
Stream output updates via ActionCable
127 128 129 130 131 |
# File 'app/models/action_agent/agent_run.rb', line 127 def broadcast_update payload = { type: "update", run: summary } ActionCable.server.broadcast("agent_run_#{id}", payload) ActionCable.server.broadcast("agent_runs_#{agent_id}", payload) end |
#calculated_duration_ms ⇒ Object
Calculate duration if not set
89 90 91 92 93 94 |
# File 'app/models/action_agent/agent_run.rb', line 89 def calculated_duration_ms return duration_ms if duration_ms.present? return nil unless started_at && completed_at ((completed_at - started_at) * 1000).to_i end |
#cancel! ⇒ Object
Cancel a running execution
134 135 136 137 138 139 140 141 142 143 |
# File 'app/models/action_agent/agent_run.rb', line 134 def cancel! return unless in_progress? update!( status: :cancelled, completed_at: Time.current, error_message: "Cancelled by user" ) broadcast_update end |
#finished? ⇒ Boolean
Check if run is finished
102 103 104 |
# File 'app/models/action_agent/agent_run.rb', line 102 def finished? complete? || failed? || cancelled? end |
#in_progress? ⇒ Boolean
Check if run is still in progress
97 98 99 |
# File 'app/models/action_agent/agent_run.rb', line 97 def in_progress? pending? || running? end |
#instructions_codename ⇒ Object
80 81 82 83 84 85 86 |
# File 'app/models/action_agent/agent_run.rb', line 80 def instructions_codename digest = instructions_digest return nil unless digest value = digest.to_i(16) "#{CODENAME_ADJECTIVES[value % 32]}-#{CODENAME_NOUNS[(value / 32) % 32]}" end |
#instructions_digest ⇒ Object
Stable short fingerprint of the instructions this run executed under — the grouping key (with model) for configuration cohorts when comparing instruction/model changes.
57 58 59 60 61 62 |
# File 'app/models/action_agent/agent_run.rb', line 57 def instructions_digest instructions = &.dig("instructions") return nil if instructions.blank? Digest::SHA256.hexdigest(instructions).first(8) end |
#summary ⇒ Object
Get a summary for display
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'app/models/action_agent/agent_run.rb', line 107 def summary { id: id, status: status, input_preview: input_prompt&.truncate(100), output_preview: output&.truncate(200), duration_ms: calculated_duration_ms, tokens: total_tokens, provider: &.dig("provider"), model: &.dig("model"), action_name: action_name || &.dig("action") || "ask", instructions_digest: instructions_digest, instructions_codename: instructions_codename, instructions_preview: &.dig("instructions")&.truncate(120), created_at: created_at, error: } end |