fix(fireworks_ai): restore Content-Type application/json header (fixes 415) (#33929)

* fix(fireworks_ai): set Content-Type application/json in validate_environment

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(fireworks_ai): delegate chat validate_environment to OpenAIGPTConfig

Instead of re-adding the JSON Content-Type default inside FireworksAIMixin,
FireworksAIConfig now delegates header construction to OpenAIGPTConfig and only
layers the Fireworks-specific x-session-affinity header on top, so the
Content-Type default can no longer drift away from the OpenAI base and reintroduce
the 415.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fireworks_ai): cover missing api key error path in chat validate_environment

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit 3fcd19d7ad)
This commit is contained in:
devin-ai-integration[bot] 2026-07-20 09:52:35 -07:00 • committed by Yuneng Jiang
parent 5d4c4d0fce
commit d75a0716d8
No known key found for this signature in database
3 changed files with 123 additions and 6 deletions

View file

@ -133,6 +133,32 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig):
def get_config(cls):
return super().get_config()
def validate_environment(
self,
headers: dict,
model: str,
messages: List[AllMessageValues],
optional_params: dict,
litellm_params: dict,
api_key: str | None = None,
api_base: str | None = None,
) -> dict:
api_key = self._get_api_key(api_key)
if api_key is None:
raise ValueError("FIREWORKS_API_KEY is not set")
validated_headers = OpenAIGPTConfig.validate_environment(
self,
headers=headers,
model=model,
messages=messages,
optional_params=optional_params,
litellm_params=litellm_params,
api_key=api_key,
api_base=api_base,
)
return self._add_session_affinity_header(validated_headers, litellm_params)
def get_supported_openai_params(self, model: str):
# Base parameters supported by all models
supported_params = [

View file

@ -64,9 +64,16 @@ class FireworksAIMixin:
if api_key is None:
raise ValueError("FIREWORKS_API_KEY is not set")
validated_headers = {"Authorization": "Bearer {}".format(api_key), **headers}
if not any(key.lower() == "x-session-affinity" for key in validated_headers):
session_id = get_fireworks_session_id(litellm_params)
if session_id:
validated_headers["x-session-affinity"] = session_id
return validated_headers
auth_headers = {"Authorization": "Bearer {}".format(api_key), **headers}
content_type_header = (
{} if any(key.lower() == "content-type" for key in auth_headers) else {"Content-Type": "application/json"}
)
return self._add_session_affinity_header({**auth_headers, **content_type_header}, litellm_params)
def _add_session_affinity_header(self, headers: dict, litellm_params: dict) -> dict:
if any(key.lower() == "x-session-affinity" for key in headers):
return headers
session_id = get_fireworks_session_id(litellm_params)
if not session_id:
return headers
return {**headers, "x-session-affinity": session_id}

View file

@ -123,6 +123,90 @@ def test_validate_environment_preserves_explicit_session_affinity_header():
assert headers["x-session-affinity"] == "explicit-session"
def test_validate_environment_sets_json_content_type():
config = FireworksAIConfig()
headers = config.validate_environment(
headers={},
model="accounts/fireworks/models/test-model",
messages=[],
optional_params={},
litellm_params={},
api_key="test-key",
)
assert headers["Content-Type"] == "application/json"
def test_validate_environment_preserves_explicit_content_type():
config = FireworksAIConfig()
headers = config.validate_environment(
headers={"content-type": "multipart/form-data"},
model="accounts/fireworks/models/test-model",
messages=[],
optional_params={},
litellm_params={},
api_key="test-key",
)
assert headers["content-type"] == "multipart/form-data"
assert "Content-Type" not in headers
def test_validate_environment_sets_json_content_type_with_session_affinity():
config = FireworksAIConfig()
headers = config.validate_environment(
headers={},
model="accounts/fireworks/models/test-model",
messages=[],
optional_params={},
litellm_params={"litellm_session_id": "session-123"},
api_key="test-key",
)
assert headers["Content-Type"] == "application/json"
assert headers["Authorization"] == "Bearer test-key"
assert headers["x-session-affinity"] == "session-123"
def test_validate_environment_resolves_api_key_from_env_and_sets_content_type(monkeypatch):
monkeypatch.setenv("FIREWORKS_API_KEY", "fw-env-key")
config = FireworksAIConfig()
headers = config.validate_environment(
headers={},
model="accounts/fireworks/models/test-model",
messages=[],
optional_params={},
litellm_params={},
)
assert headers["Authorization"] == "Bearer fw-env-key"
assert headers["Content-Type"] == "application/json"
def test_validate_environment_raises_without_api_key(monkeypatch):
for env_var in (
"FIREWORKS_API_KEY",
"FIREWORKS_AI_API_KEY",
"FIREWORKSAI_API_KEY",
"FIREWORKS_AI_TOKEN",
):
monkeypatch.delenv(env_var, raising=False)
config = FireworksAIConfig()
with pytest.raises(ValueError, match="FIREWORKS_API_KEY is not set"):
config.validate_environment(
headers={},
model="accounts/fireworks/models/test-model",
messages=[],
optional_params={},
litellm_params={},
)
def test_get_fireworks_session_id_prefers_litellm_session_id_over_trace_id():
assert (
get_fireworks_session_id(