Skip to content

Commit c490de2

Browse files
committed
Update guardrail pipeline steps to use class methods
This brings these inline with the rest of the pipeline steps and allows them to be called without needing to instantiate an instance of the class.
1 parent 4e07c4a commit c490de2

6 files changed

Lines changed: 33 additions & 39 deletions

File tree

lib/answer_composition/pipeline/answer_guardrails.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module AnswerComposition
22
module Pipeline
33
class AnswerGuardrails < OutputGuardrails
4-
def call(context)
4+
def call
55
start_time = Clock.monotonic_time
66
response = generate_response(context)
77

lib/answer_composition/pipeline/output_guardrails.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
module AnswerComposition
22
module Pipeline
33
class OutputGuardrails
4-
def initialize(llm_provider: :claude)
5-
@llm_provider = llm_provider
4+
attr_reader :context
5+
6+
def self.call(...) = new(...).call
7+
8+
def initialize(context)
9+
@context = context
610
end
711

812
protected
913

10-
attr_reader :llm_provider
11-
1214
def build_metrics(start_time, response_or_error)
1315
{
1416
duration: Clock.monotonic_time - start_time,

lib/answer_composition/pipeline/question_routing_guardrails.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module AnswerComposition
22
module Pipeline
33
class QuestionRoutingGuardrails < OutputGuardrails
4-
def call(context)
4+
def call
55
return if context.answer.question_routing_label == "genuine_rag"
66

77
start_time = Clock.monotonic_time

spec/lib/answer_composition/pipeline/answer_guardrails_spec.rb

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
RSpec.describe AnswerComposition::Pipeline::AnswerGuardrails do
22
let(:context) { build(:answer_pipeline_context) }
33
let(:message) { "sample answer message" }
4+
let(:guardrail_response) { build(:guardrails_multiple_checker_result, :pass) }
45

56
before do
67
context.answer.message = message
78
allow(Guardrails::MultipleChecker).to receive(:call).and_return(guardrail_response)
89
end
910

10-
context "when the llm_provider is :claude" do
11-
let(:llm_provider) { :claude }
12-
let(:guardrail_response) { build(:guardrails_multiple_checker_result, :pass) }
13-
14-
it "initializes the calls Guardrails::MultipleChecker with Claude as the provider" do
15-
described_class.new(llm_provider: llm_provider).call(context)
16-
expect(Guardrails::MultipleChecker).to have_received(:call).with(message, "answer_guardrails")
17-
end
11+
it "calls the Guardrails::MultipleChecker with the correct parameters" do
12+
described_class.call(context)
13+
expect(Guardrails::MultipleChecker).to have_received(:call).with(message, "answer_guardrails")
1814
end
1915

2016
context "when the guardrails are not triggered" do
@@ -23,7 +19,7 @@
2319
it_behaves_like "a passing guardrail pipeline step", "answer_guardrails"
2420

2521
it "does not abort the pipeline" do
26-
described_class.new(llm_provider: :claude).call(context)
22+
described_class.call(context)
2723
expect(context.aborted?).to be false
2824
end
2925
end
@@ -33,7 +29,7 @@
3329

3430
it "aborts the pipeline and updates the answer's status and message attributes" do
3531
expect {
36-
described_class.new(llm_provider: :claude).call(context)
32+
described_class.call(context)
3733
}.to throw_symbol(:abort)
3834

3935
expect(context.answer).to have_attributes(
@@ -45,14 +41,14 @@
4541
end
4642

4743
it "assigns the llm response to the answer" do
48-
expect { described_class.new(llm_provider: :claude).call(context) }.to throw_symbol(:abort)
44+
expect { described_class.call(context) }.to throw_symbol(:abort)
4945
expect(context.answer.llm_responses["answer_guardrails"]).to eq(guardrail_response.llm_response)
5046
end
5147

5248
it "assigns metrics to the answer" do
5349
allow(Clock).to receive(:monotonic_time).and_return(100.0, 101.5)
5450

55-
expect { described_class.new(llm_provider: :claude).call(context) }.to throw_symbol(:abort)
51+
expect { described_class.call(context) }.to throw_symbol(:abort)
5652

5753
expect(context.answer.metrics["answer_guardrails"]).to eq({
5854
duration: 1.5,

spec/lib/answer_composition/pipeline/question_routing_guardrails_spec.rb

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
RSpec.describe AnswerComposition::Pipeline::QuestionRoutingGuardrails do
22
let(:context) { build(:answer_pipeline_context) }
33
let(:message) { "sample answer message" }
4+
let(:guardrail_response) { build(:guardrails_multiple_checker_result, :pass) }
45

56
before do
67
context.answer.message = message
78
allow(Guardrails::MultipleChecker).to receive(:call).and_return(guardrail_response)
89
end
910

10-
context "when the llm_provider is :claude" do
11-
let(:llm_provider) { :claude }
12-
let(:guardrail_response) { build(:guardrails_multiple_checker_result, :pass) }
13-
14-
it "initializes the calls Guardrails::MultipleChecker with Claude as the provider" do
15-
described_class.new(llm_provider: llm_provider).call(context)
16-
expect(Guardrails::MultipleChecker).to have_received(:call).with(message, "question_routing_guardrails", llm_provider)
17-
end
11+
it "calls the Guardrails::MultipleChecker with the correct parameters" do
12+
described_class.call(context)
13+
expect(Guardrails::MultipleChecker).to have_received(:call).with(message, "question_routing_guardrails")
1814
end
1915

2016
context "when the guardrails are not triggered" do
@@ -27,11 +23,11 @@
2723

2824
context.answer.question_routing_label = "genuine_rag"
2925

30-
described_class.new(llm_provider: :claude).call(context)
26+
described_class.call(context)
3127
end
3228

3329
it "aborts the pipeline" do
34-
described_class.new(llm_provider: :claude).call(context)
30+
described_class.call(context)
3531
expect(context.aborted?).to be true
3632
end
3733
end
@@ -40,7 +36,7 @@
4036
let(:guardrail_response) { build(:guardrails_multiple_checker_result, :fail) }
4137

4238
it "sets the attributes on the answer" do
43-
described_class.new(llm_provider: :claude).call(context)
39+
described_class.call(context)
4440

4541
expect(context.answer).to have_attributes({
4642
message: Answer::CannedResponses::QUESTION_ROUTING_GUARDRAILS_FAILED_MESSAGE,
@@ -50,7 +46,7 @@
5046
end
5147

5248
it "aborts the pipeline and assigns the right attributes" do
53-
described_class.new(llm_provider: :claude).call(context)
49+
described_class.call(context)
5450

5551
expect(context.aborted?).to be true
5652
expect(context.answer.question_routing_guardrails_status).to eq("fail")

spec/support/guardrails_examples.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
module GuardrailsExamples
22
shared_examples "a passing guardrail pipeline step" do |guardrail_name|
33
it "calls the guardrails with the answer message" do
4-
described_class.new.call(context)
4+
described_class.call(context)
55
expect(Guardrails::MultipleChecker)
66
.to have_received(:call)
7-
.with(context.answer.message, guardrail_name, :claude)
7+
.with(context.answer.message, guardrail_name)
88
end
99

1010
it "does not change the message" do
11-
expect { described_class.new.call(context) }.not_to change(context.answer, :message)
11+
expect { described_class.call(context) }.not_to change(context.answer, :message)
1212
end
1313

1414
it "sets the status" do
15-
expect { described_class.new.call(context) }
15+
expect { described_class.call(context) }
1616
.to change(context.answer, "#{guardrail_name}_status").to("pass")
1717
end
1818

1919
it "assigns the llm response to the answer" do
20-
described_class.new.call(context)
20+
described_class.call(context)
2121

2222
expect(context.answer.llm_responses[guardrail_name])
2323
.to eq(guardrail_response.llm_response)
@@ -26,7 +26,7 @@ module GuardrailsExamples
2626
it "assigns metrics to the answer" do
2727
allow(Clock).to receive(:monotonic_time).and_return(100.0, 101.5)
2828

29-
described_class.new.call(context)
29+
described_class.call(context)
3030

3131
expect(context.answer.metrics[guardrail_name]).to eq({
3232
duration: 1.5,
@@ -64,7 +64,7 @@ module GuardrailsExamples
6464
end
6565

6666
it "aborts the pipeline and updates the answer's status with an error message" do
67-
expect { described_class.new.call(context) }.to throw_symbol(:abort)
67+
expect { described_class.call(context) }.to throw_symbol(:abort)
6868
expect(context.answer).to have_attributes(
6969
message:,
7070
status: "error_#{guardrail_name}",
@@ -73,14 +73,14 @@ module GuardrailsExamples
7373
end
7474

7575
it "assigns the llm response to the answer" do
76-
expect { described_class.new.call(context) }.to throw_symbol(:abort)
76+
expect { described_class.call(context) }.to throw_symbol(:abort)
7777
expect(context.answer.llm_responses[guardrail_name]).to eq(llm_response)
7878
end
7979

8080
it "assigns metrics to the answer" do
8181
allow(Clock).to receive(:monotonic_time).and_return(100.0, 101.5)
8282

83-
expect { described_class.new.call(context) }.to throw_symbol(:abort)
83+
expect { described_class.call(context) }.to throw_symbol(:abort)
8484

8585
expect(context.answer.metrics[guardrail_name]).to eq({
8686
duration: 1.5,

0 commit comments

Comments
 (0)