actionagent 1.9.0 → 1.9.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/app/assets/builds/action_agent.js +59 -59
- data/app/controllers/action_agent/api/feedback_ingest_controller.rb +34 -0
- data/app/controllers/action_agent/api/feedbacks_controller.rb +40 -0
- data/app/models/action_agent/interaction_feedback.rb +24 -0
- data/config/routes.rb +3 -0
- data/lib/action_agent/version.rb +1 -1
- data/lib/generators/action_agent/templates/migrations/027_create_active_agent_interaction_feedbacks.rb.erb +17 -0
- metadata +6 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAgent
|
|
4
|
+
module Api
|
|
5
|
+
# Server-to-server submission; end-user apps authenticate their own users
|
|
6
|
+
# and proxy feedback rather than exposing the ingest key in a browser.
|
|
7
|
+
class FeedbackIngestController < ActionController::API
|
|
8
|
+
include IngestAuthentication
|
|
9
|
+
|
|
10
|
+
def create
|
|
11
|
+
envelope = params.require(:feedback)
|
|
12
|
+
raise ActionController::ParameterMissing, :feedback unless envelope.is_a?(ActionController::Parameters)
|
|
13
|
+
|
|
14
|
+
input = envelope.permit(:trace_id, :feedback_id, :rating, :comment, :span_id, :user_reference)
|
|
15
|
+
trace = ActionAgent.trace_model.for_account(@account).find_by!(trace_id: input.delete(:trace_id))
|
|
16
|
+
record = nil
|
|
17
|
+
# Serialize concurrent changes and use the unique index for first-write
|
|
18
|
+
# races. Reusing an ID deliberately replaces the prior rating/comment.
|
|
19
|
+
trace.with_lock do
|
|
20
|
+
record = InteractionFeedback.find_or_initialize_by(
|
|
21
|
+
telemetry_trace_id: trace.id, feedback_id: input[:feedback_id])
|
|
22
|
+
record.update!(input)
|
|
23
|
+
end
|
|
24
|
+
render json: { feedback: record.as_json }, status: :ok
|
|
25
|
+
rescue ActiveRecord::RecordNotFound
|
|
26
|
+
render json: { error: "Trace not found; retry after trace ingestion completes" }, status: :not_found
|
|
27
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
28
|
+
render json: { error: e.record.errors.full_messages }, status: :unprocessable_entity
|
|
29
|
+
rescue ActionController::ParameterMissing => e
|
|
30
|
+
render json: { error: e.message }, status: :bad_request
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAgent
|
|
4
|
+
module Api
|
|
5
|
+
class FeedbacksController < BaseController
|
|
6
|
+
before_action :require_owner!
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
traces = owned_traces
|
|
10
|
+
traces = owner_agents.find(params[:agent_id]).telemetry_traces(traces) if params[:agent_id].present?
|
|
11
|
+
scope = InteractionFeedback.where(telemetry_trace_id: traces.select(:id))
|
|
12
|
+
if params[:rating].present?
|
|
13
|
+
return render(json: { error: "Invalid rating" }, status: :bad_request) unless %w[positive negative].include?(params[:rating])
|
|
14
|
+
scope = scope.where(rating: params[:rating])
|
|
15
|
+
end
|
|
16
|
+
scope = scope.where("created_at >= ?", date_start(params[:from])) if params[:from].present?
|
|
17
|
+
scope = scope.where("created_at < ?", date_start(params[:to]) + 1.day) if params[:to].present?
|
|
18
|
+
counts = scope.group(:rating).count
|
|
19
|
+
total = counts.values.sum
|
|
20
|
+
page = clamped_param(:page, default: 1, min: 1, max: 100_000)
|
|
21
|
+
rows = scope.order(created_at: :desc, id: :desc).offset((page - 1) * 50).limit(50)
|
|
22
|
+
trace_ids = ActionAgent.trace_model.where(id: rows.map(&:telemetry_trace_id)).pluck(:id, :trace_id).to_h
|
|
23
|
+
render json: {
|
|
24
|
+
feedback: rows.map { |row| row.as_json.merge("trace_id" => trace_ids[row.telemetry_trace_id]) },
|
|
25
|
+
page: page, total: total, positive: counts.fetch("positive", 0), negative: counts.fetch("negative", 0),
|
|
26
|
+
positive_percentage: total.zero? ? nil : (100.0 * counts.fetch("positive", 0) / total).round(1)
|
|
27
|
+
}
|
|
28
|
+
rescue Date::Error
|
|
29
|
+
render json: { error: "Dates must use YYYY-MM-DD" }, status: :bad_request
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def date_start(value)
|
|
35
|
+
date = Date.iso8601(value.to_s)
|
|
36
|
+
Time.zone.local(date.year, date.month, date.day)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionAgent
|
|
4
|
+
# Feedback is attached to a trace primary key, so tenant ownership follows
|
|
5
|
+
# the trace even when two tenants report the same external trace_id.
|
|
6
|
+
class InteractionFeedback < ApplicationRecord
|
|
7
|
+
belongs_to :telemetry_trace, class_name: "ActionAgent::TelemetryTrace"
|
|
8
|
+
validates :feedback_id, presence: true, length: { maximum: 128 }, uniqueness: { scope: :telemetry_trace_id }
|
|
9
|
+
validates :rating, inclusion: { in: %w[positive negative] }
|
|
10
|
+
validates :comment, length: { maximum: 5000 }
|
|
11
|
+
validates :span_id, length: { maximum: 128 }
|
|
12
|
+
validates :user_reference, length: { maximum: 255 }
|
|
13
|
+
validate :span_belongs_to_trace
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def span_belongs_to_trace
|
|
18
|
+
return if span_id.blank? || telemetry_trace.nil?
|
|
19
|
+
return if Array(telemetry_trace.spans).any? { |span| span["span_id"] == span_id }
|
|
20
|
+
|
|
21
|
+
errors.add(:span_id, "must belong to the trace")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/config/routes.rb
CHANGED
|
@@ -32,6 +32,9 @@ ActionAgent::Engine.routes.draw do
|
|
|
32
32
|
# Authenticated like trace ingest, with a bearer token.
|
|
33
33
|
resources :evaluation_reports, only: [ :create ]
|
|
34
34
|
|
|
35
|
+
post "feedback", to: "feedback_ingest#create"
|
|
36
|
+
get "feedback", to: "feedbacks#index"
|
|
37
|
+
|
|
35
38
|
# A JSON API has no :new or :edit forms to serve.
|
|
36
39
|
resources :agents, except: [ :new, :edit ] do
|
|
37
40
|
member do
|
data/lib/action_agent/version.rb
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateActiveAgentInteractionFeedbacks < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
create_table "#{ActionAgent.table_name_prefix}interaction_feedbacks" do |t|
|
|
6
|
+
t.references :telemetry_trace, null: false, foreign_key: { to_table: :active_agent_telemetry_traces, on_delete: :cascade }
|
|
7
|
+
t.string :feedback_id, null: false, limit: 128
|
|
8
|
+
t.string :rating, null: false, limit: 16
|
|
9
|
+
t.string :span_id, limit: 128
|
|
10
|
+
t.string :user_reference, limit: 255
|
|
11
|
+
t.text :comment
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
add_index "#{ActionAgent.table_name_prefix}interaction_feedbacks",
|
|
15
|
+
[ :telemetry_trace_id, :feedback_id ], unique: true, name: "index_feedback_on_trace_and_feedback_id"
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: actionagent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.9.
|
|
4
|
+
version: 1.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Bowen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activeagent
|
|
@@ -139,6 +139,8 @@ files:
|
|
|
139
139
|
- app/controllers/action_agent/api/evaluation_reports_controller.rb
|
|
140
140
|
- app/controllers/action_agent/api/evaluations_controller.rb
|
|
141
141
|
- app/controllers/action_agent/api/explorations_controller.rb
|
|
142
|
+
- app/controllers/action_agent/api/feedback_ingest_controller.rb
|
|
143
|
+
- app/controllers/action_agent/api/feedbacks_controller.rb
|
|
142
144
|
- app/controllers/action_agent/api/github_app_manifests_controller.rb
|
|
143
145
|
- app/controllers/action_agent/api/github_connections_controller.rb
|
|
144
146
|
- app/controllers/action_agent/api/github_installations_controller.rb
|
|
@@ -221,6 +223,7 @@ files:
|
|
|
221
223
|
- app/models/action_agent/github_connection.rb
|
|
222
224
|
- app/models/action_agent/github_installation.rb
|
|
223
225
|
- app/models/action_agent/input_request.rb
|
|
226
|
+
- app/models/action_agent/interaction_feedback.rb
|
|
224
227
|
- app/models/action_agent/model_pricing.rb
|
|
225
228
|
- app/models/action_agent/project.rb
|
|
226
229
|
- app/models/action_agent/project_secret.rb
|
|
@@ -354,6 +357,7 @@ files:
|
|
|
354
357
|
- lib/generators/action_agent/templates/migrations/024_add_kind_to_active_agent_project_secrets.rb.erb
|
|
355
358
|
- lib/generators/action_agent/templates/migrations/025_create_active_agent_scenario_catalogs.rb.erb
|
|
356
359
|
- lib/generators/action_agent/templates/migrations/026_add_evaluation_fixes_to_code_sessions.rb.erb
|
|
360
|
+
- lib/generators/action_agent/templates/migrations/027_create_active_agent_interaction_feedbacks.rb.erb
|
|
357
361
|
- lib/tasks/action_agent.rake
|
|
358
362
|
- lib/tasks/browser.rake
|
|
359
363
|
- lib/tasks/claude_code.rake
|