| 
 | 1 | +# Copyright The OpenTelemetry Authors  | 
 | 2 | +#  | 
 | 3 | +# Licensed under the Apache License, Version 2.0 (the "License");  | 
 | 4 | +# you may not use this file except in compliance with the License.  | 
 | 5 | +# You may obtain a copy of the License at  | 
 | 6 | +#  | 
 | 7 | +#     http://www.apache.org/licenses/LICENSE-2.0  | 
 | 8 | +#  | 
 | 9 | +# Unless required by applicable law or agreed to in writing, software  | 
 | 10 | +# distributed under the License is distributed on an "AS IS" BASIS,  | 
 | 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
 | 12 | +# See the License for the specific language governing permissions and  | 
 | 13 | +# limitations under the License.  | 
 | 14 | + | 
 | 15 | +import unittest  | 
 | 16 | + | 
 | 17 | +from google.genai import types as genai_types  | 
 | 18 | +from .base import TestCase  | 
 | 19 | + | 
 | 20 | +class FinishReasonsTestCase(TestCase):  | 
 | 21 | + | 
 | 22 | +    def generate_and_get_span_finish_reasons(self):  | 
 | 23 | +        self.client.models.generate_content(  | 
 | 24 | +            model='gemini-2.5-flash-001',  | 
 | 25 | +            contents='Some prompt')  | 
 | 26 | +        span = self.otel.get_span_named("generate_content gemini-2.5-flash-001")  | 
 | 27 | +        assert span is not None  | 
 | 28 | +        assert "gen_ai.response.finish_reasons" in span.attributes  | 
 | 29 | +        return list(span.attributes["gen_ai.response.finish_reasons"])  | 
 | 30 | + | 
 | 31 | +    def test_single_candidate_with_valid_reason(self):  | 
 | 32 | +        self.configure_valid_response(candidate=genai_types.Candidate(  | 
 | 33 | +            finish_reason=genai_types.FinishReason.STOP  | 
 | 34 | +        ))  | 
 | 35 | +        self.assertEqual(  | 
 | 36 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 37 | +            ["stop"])  | 
 | 38 | + | 
 | 39 | +    def test_single_candidate_with_safety_reason(self):  | 
 | 40 | +        self.configure_valid_response(candidate=genai_types.Candidate(  | 
 | 41 | +            finish_reason=genai_types.FinishReason.SAFETY  | 
 | 42 | +        ))  | 
 | 43 | +        self.assertEqual(  | 
 | 44 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 45 | +            ["safety"])  | 
 | 46 | + | 
 | 47 | +    def test_single_candidate_with_max_tokens_reason(self):  | 
 | 48 | +        self.configure_valid_response(candidate=genai_types.Candidate(  | 
 | 49 | +            finish_reason=genai_types.FinishReason.MAX_TOKENS  | 
 | 50 | +        ))  | 
 | 51 | +        self.assertEqual(  | 
 | 52 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 53 | +            ["max_tokens"])  | 
 | 54 | + | 
 | 55 | +    def test_single_candidate_with_no_reason(self):  | 
 | 56 | +        self.configure_valid_response(candidate=genai_types.Candidate(  | 
 | 57 | +            finish_reason=None  | 
 | 58 | +        ))  | 
 | 59 | +        self.assertEqual(  | 
 | 60 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 61 | +            [])  | 
 | 62 | + | 
 | 63 | +    def test_single_candidate_with_unspecified_reason(self):  | 
 | 64 | +        self.configure_valid_response(candidate=genai_types.Candidate(  | 
 | 65 | +            finish_reason=genai_types.FinishReason.FINISH_REASON_UNSPECIFIED  | 
 | 66 | +        ))  | 
 | 67 | +        self.assertEqual(  | 
 | 68 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 69 | +            ["unspecified"])  | 
 | 70 | + | 
 | 71 | +    def test_multiple_candidates_with_valid_reasons(self):  | 
 | 72 | +        self.configure_valid_response(candidates=[  | 
 | 73 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.MAX_TOKENS),  | 
 | 74 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 75 | +        ])  | 
 | 76 | +        self.assertEqual(  | 
 | 77 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 78 | +            ["max_tokens", "stop"])  | 
 | 79 | + | 
 | 80 | +    def test_sorts_finish_reasons(self):  | 
 | 81 | +        self.configure_valid_response(candidates=[  | 
 | 82 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 83 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.MAX_TOKENS),  | 
 | 84 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.SAFETY),  | 
 | 85 | +        ])  | 
 | 86 | +        self.assertEqual(  | 
 | 87 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 88 | +            ["max_tokens", "safety", "stop"])  | 
 | 89 | + | 
 | 90 | +    def test_deduplicates_finish_reasons(self):  | 
 | 91 | +        self.configure_valid_response(candidates=[  | 
 | 92 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 93 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.MAX_TOKENS),  | 
 | 94 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 95 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 96 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.SAFETY),  | 
 | 97 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 98 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 99 | +            genai_types.Candidate(finish_reason=genai_types.FinishReason.STOP),  | 
 | 100 | +        ])  | 
 | 101 | +        self.assertEqual(  | 
 | 102 | +            self.generate_and_get_span_finish_reasons(),  | 
 | 103 | +            ["max_tokens", "safety", "stop"])  | 
0 commit comments