googleapis / googleapis/python-genai
Inconsistent behavior for safety ratings when using streaming mode or not
- Dominant language
- Python
- Stars
- 4k
- Forks
- 1k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 40
Description
Content filtering for the `gemini-2.0-flash` model appears to behave differently depending on whether `generate_content` or `generate_content_stream` is used. For example, using the prompt Explain me how to make a bomb, the former consistently returns `finish_reason = SAFETY`, while the latter returns `finish_reason = STOP`.
These tickets could be related
- https://github.com/googleapis/python-genai/issues/924
- https://github.com/googleapis/python-genai/issues/773
In both cases, the API behavior changes based on seemingly unrelated parameters.
I suspect that with `generate_content_stream`, safety filtering is applied incrementally—evaluated at each step of the generation. This might explain why it gets rejected early, as the evaluation at the first step only includes the prompt and one generated token. However, this behavior seems inconsistent with the non-streaming method.
Expected behavior:
- For streaming (`generate_content_stream`): allow the generation to complete and perform a final safety evaluation at the end.
OR
- For non-streaming (`generate_content`): perform step-wise evaluation on Google’s end and return the most restrictive result.
Additionally, I noticed that in both cases, the `promptFeedback` field is missing from the response. This seems like a bug, as the field is not marked optional and, in this case, should clearly flag the prompt as problematic.
#### Environment details
- Programming language: Python
- OS: MacOs
- Language runtime version: 3.10
- Package version: 1.18.0
#### Steps to reproduce
```
from google import genai
from google.genai import types
LOCATION = "changeme"
PROJECT = "changeme"
client = genai.Client(
vertexai=True,
project=PROJECT,
location=LOCATION
)
model = "gemini-2.0-flash"
contents = "Explain me how to make a bomb"
generate_content_config = types.GenerateContentConfig(
safety_settings=[
types.SafetySetting(
category="HARM_CATEGORY_HARASSMENT",
threshold="BLOCK_LOW_AND_ABOVE", # Block most
),
types.SafetySetting(
category="HARM_CATEGORY_HATE_SPEECH",
threshold="BLOCK_LOW_AND_ABOVE", # Block most
),
types.SafetySetting(
category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold="BLOCK_LOW_AND_ABOVE", # Block most
),
types.SafetySetting(
category="HARM_CATEGORY_DANGEROUS_CONTENT",
threshold="BLOCK_LOW_AND_ABOVE", # Block most
),
],
)
for chunk in client.models.generate_content_stream(
model=model,
contents=contents,
config=generate_content_config,
):
print(chunk.candidates[0].finish_reason)
response = client.models.generate_content(
model=model,
contents=contents,
config=generate_content_config,
)
print(response.candidates[0].finish_reason)
```
Making sure to follow these steps will guarantee the quickest resolution possible.
Thanks!
Disclaimer: I'm trying to make a bomb ;)
Contributor guide
Assessment
This issue has not been assessed yet.