Skip to content

Strip mistral-specific fields in SpeechRequest.to_openai - #290

Open
jaideeppyne wants to merge 1 commit into
mistralai:mainfrom
jaideeppyne:fix/speech-to-openai-strip-internal-fields
Open

Strip mistral-specific fields in SpeechRequest.to_openai#290
jaideeppyne wants to merge 1 commit into
mistralai:mainfrom
jaideeppyne:fix/speech-to-openai-strip-internal-fields

Conversation

@jaideeppyne

Copy link
Copy Markdown

What

SpeechRequest.to_openai() leaks mistral-internal fields (id, max_tokens) into the OpenAI-compatible request payload, while its sibling TranscriptionRequest.to_openai() deliberately strips them.

SpeechRequest(id="req-123", input="hi", voice="female", model="tts-1").to_openai()
# {'id': 'req-123', 'max_tokens': None, 'temperature': 0.7, 'top_p': 1.0, 'model': ..., 'input': ..., 'voice': ..., 'seed': ...}
#  ^^^^ leaked                ^^^^^^^^^^ leaked

Why

SpeechRequest.to_openai does self.model_dump(...) and returns it directly, so every internal field ends up in the OpenAI request. TranscriptionRequest.to_openai already handles exactly this:

# transcription/request.py
# remove mistral-specific
# TODO: revisit which fields to expose in the OpenAI format
default_exclude = ("id", "max_tokens", "strict_audio_validation", "streaming")
default_exclude += exclude
for k in default_exclude:
    openai_request.pop(k, None)

So the two to_openai conversions are inconsistent, and a SpeechRequest's internal id / max_tokens are sent to the OpenAI endpoint.

How

Apply the same exclusion in SpeechRequest.to_openai, and add an exclude parameter for parity with TranscriptionRequest.to_openai:

-    def to_openai(self, **kwargs: Any) -> dict[str, Any]:
+    def to_openai(self, exclude: tuple = (), **kwargs: Any) -> dict[str, Any]:
         ...
         openai_request["seed"] = openai_request.pop("random_seed")
         openai_request.update(kwargs)
+
+        # remove mistral-specific fields, mirroring TranscriptionRequest.to_openai
+        # TODO: revisit which fields to expose in the OpenAI format
+        default_exclude = ("id", "max_tokens", "strict_audio_validation", "streaming")
+        default_exclude += exclude
+        for k in default_exclude:
+            openai_request.pop(k, None)

(I kept the exclusion list identical to TranscriptionRequest so the two stay in sync; strict_audio_validation/streaming simply aren't present on SpeechRequest and pop(..., None) is a no-op for them.)

Tests

Added test_speech_to_openai_excludes_mistral_specific_fields: asserts id/max_tokens are stripped, the OpenAI fields (model/input/voice) are preserved, and the exclude argument is honored. It fails on main (id is present) and passes with this change; the speech to_openai tests stay green (10 passed). ruff check is clean.


Disclosure: this change was prepared with AI assistance and reviewed/verified by me before submission.

SpeechRequest.to_openai dumped the whole model and returned it as the
OpenAI-compatible request, leaking mistral-internal fields (id, max_tokens)
into the payload. Its sibling TranscriptionRequest.to_openai already removes
these (default_exclude = ('id', 'max_tokens', 'strict_audio_validation',
'streaming')), so the two conversions were inconsistent and a SpeechRequest's
internal id/max_tokens ended up in the OpenAI request.

Apply the same exclusion in SpeechRequest.to_openai and add an 'exclude'
parameter for parity with TranscriptionRequest.to_openai.

Adds a regression test asserting id/max_tokens are stripped, the OpenAI fields
are preserved, and the 'exclude' argument is honored.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant