Skip to content

Commit d4ae892

Browse files
committed
Add coherence to the question show page in the admin UI
This adds the coherence evaluation runs to the analysis tab of the question show page in the admin interface. It mirrors the existing answer relevancy runs display.
1 parent b5bdb89 commit d4ae892

6 files changed

Lines changed: 71 additions & 56 deletions

File tree

app/controllers/admin/questions_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def index
77
def show
88
question_scope = Question.includes(
99
conversation: :signon_user,
10-
answer: [{ sources: :chunk }, :feedback, :topics, :answer_relevancy_runs],
10+
answer: [{ sources: :chunk }, :feedback, :topics, :answer_relevancy_runs, :coherence_runs],
1111
)
1212

1313
@question = question_scope.find(params[:id])

app/models/answer.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ def group_used_answer_sources_by_base_path
203203
end
204204

205205
def has_analysis?
206-
topics.present? || answer_relevancy_runs.present?
206+
topics.present? ||
207+
answer_relevancy_runs.present? ||
208+
coherence_runs.present?
207209
end
208210

209211
def question_used

app/views/admin/questions/_analysis_tab.html.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@
4949
<% if answer_relevancy_runs.present? %>
5050
<%= render "generic_auto_evaluation_runs", runs: answer_relevancy_runs, title: "Answer relevancy" %>
5151
<% end %>
52+
53+
<% if coherence_runs.present? %>
54+
<%= render "generic_auto_evaluation_runs", runs: coherence_runs, title: "Coherence" %>
55+
<% end %>

app/views/admin/questions/show.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ content_for(:active_navigation_item, admin_questions_path)
4040
"analysis_tab",
4141
topics: @answer.topics,
4242
answer_relevancy_runs: @answer.answer_relevancy_runs,
43+
coherence_runs: @answer.coherence_runs,
4344
),
4445
} if @answer&.has_analysis?
4546
%>

spec/models/answer_spec.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,14 @@
360360
expect(answer.has_analysis?).to be(true)
361361
end
362362

363-
it "returns true if answer_relevancy_runs are present" do
364-
answer = build(
365-
:answer, answer_relevancy_runs: [build(:answer_relevancy_run)]
366-
)
367-
expect(answer.has_analysis?).to be(true)
363+
%i[answer_relevancy_runs coherence_runs].each do |run_association|
364+
it "returns true if #{run_association} are present" do
365+
answer = build(
366+
:answer, "#{run_association}": [build(run_association.to_s.singularize)]
367+
)
368+
369+
expect(answer.has_analysis?).to be(true)
370+
end
368371
end
369372

370373
it "returns false if no analysis is present" do

spec/requests/admin/questions_spec.rb

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,59 @@
175175
end
176176

177177
describe "GET :show" do
178+
shared_examples "renders_run in analysis tab when present" do |run_association, title|
179+
context "when #{run_association} data is present" do
180+
let(:run) do
181+
create(
182+
run_association,
183+
score: 0.85,
184+
reason: "The answer was acceptable.",
185+
llm_responses: {
186+
"verdicts" => { "verdicts" => [{ "verdict" => "yes" }] },
187+
"reason" => { "reason" => ["The answer was acceptable."] },
188+
189+
},
190+
metrics: {
191+
"verdicts" => { duration: 1.44445 },
192+
"reason" => { duration: 1.55556 },
193+
},
194+
)
195+
end
196+
let(:question) { run.answer.question }
197+
198+
it "renders #{run_association} details in the analysis tab" do
199+
get admin_show_question_path(question)
200+
201+
expect(response.body)
202+
.to have_selector("#analysis-tab", text: title)
203+
.and have_selector("#analysis-tab", text: /Mean score\s*0\.85/)
204+
.and have_selector("#analysis-tab", text: /Run 1 score\s*0\.85/)
205+
.and have_selector("#analysis-tab", text: /Run 1 reason\s*The answer was acceptable\./)
206+
end
207+
208+
it "renders the runs llm responses" do
209+
get admin_show_question_path(question)
210+
211+
expect(response.body.squish)
212+
.to have_content('{ "verdicts": [ { "verdict": "yes" } ] }')
213+
.and have_content('{ "reason": [ "The answer was acceptable." ] }')
214+
end
215+
216+
it "renders the runs metrics" do
217+
get admin_show_question_path(question)
218+
219+
expect(response.body.squish)
220+
.to have_content("Verdicts")
221+
.and have_content(/duration.*1\.44445/)
222+
.and have_content("Reason")
223+
.and have_content(/duration.*1\.55556/)
224+
end
225+
end
226+
end
227+
228+
it_behaves_like "renders_run in analysis tab when present", :answer_relevancy_run, "Answer relevancy"
229+
it_behaves_like "renders_run in analysis tab when present", :coherence_run, "Coherence"
230+
178231
it "renders the page successfully" do
179232
question = create(:question)
180233
get admin_show_question_path(question)
@@ -284,7 +337,7 @@
284337
.and have_content('"id": "call_dqGpbb39drQDafLsjDLtnbGD"')
285338
end
286339

287-
it "doesn't render the tabs component when there are no topics or auto-eval aggregate data" do
340+
it "doesn't render the tabs component there is no analysis data" do
288341
question = create(:question, :with_answer)
289342
get admin_show_question_path(question)
290343

@@ -361,54 +414,6 @@
361414
.and have_selector("#analysis-tab", text: topics.secondary_topic.capitalize)
362415
end
363416
end
364-
365-
context "when answer relevancy aggregate data is present" do
366-
let(:run) do
367-
create(
368-
:answer_relevancy_run,
369-
score: 0.85,
370-
reason: "The answer is relevant to the question.",
371-
llm_responses: {
372-
"statements" => { "statements" => ["The answer is relevant."] },
373-
"verdicts" => { "verdicts" => [{ "verdict" => "yes" }] },
374-
},
375-
metrics: {
376-
"statements" => { duration: 1.55556 },
377-
"verdicts" => { duration: 1.44445 },
378-
},
379-
)
380-
end
381-
let(:question) { run.answer.question }
382-
383-
it "renders the answer relevancy aggregate and run details" do
384-
get admin_show_question_path(question)
385-
386-
expect(response.body.squish)
387-
.to have_content("Answer relevancy")
388-
.and have_content("Run 1 score")
389-
.and have_content("0.85")
390-
.and have_content("Run 1 reason")
391-
.and have_content("The answer is relevant to the question.")
392-
end
393-
394-
it "renders the runs llm responses" do
395-
get admin_show_question_path(question)
396-
397-
expect(response.body.squish)
398-
.to have_content('{ "statements": [ "The answer is relevant." ] }')
399-
.and have_content('{ "verdicts": [ { "verdict": "yes" } ] }')
400-
end
401-
402-
it "renders the runs metrics" do
403-
get admin_show_question_path(question)
404-
405-
expect(response.body.squish)
406-
.to have_content("Statements")
407-
.and have_content(/duration.*1\.55556/)
408-
.and have_content("Verdicts")
409-
.and have_content(/duration.*1\.44445/)
410-
end
411-
end
412417
end
413418

414419
def expect_unprocessable_content_with_date_errors

0 commit comments

Comments
 (0)