Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions astrbot/core/provider/sources/openai_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,26 @@ def _ollama_disable_thinking_enabled(self) -> bool:
return value.strip().lower() in {"1", "true", "yes", "on"}
return bool(value)

def _apply_provider_specific_extra_body_overrides(
self, extra_body: dict[str, Any]
def _apply_provider_specific_request_overrides(
self,
payloads: dict[str, Any],
extra_body: dict[str, Any],
) -> None:
if self.provider_config.get("provider") != "ollama":
provider = self.provider_config.get("provider")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make the provider check robust and case-insensitive (consistent with how the model parameter is handled on the next line), normalize the provider name to lowercase and handle potential None values safely.

Suggested change
provider = self.provider_config.get("provider")
provider = str(self.provider_config.get("provider") or "").lower()

model = str(payloads.get("model", "")).lower()

# NVIDIA's hosted MiniMax M3 endpoint can return empty choices when
# max_tokens is omitted (#9206). Scope the compatibility default to
# that model; other NVIDIA models have different token limits.
if (
provider == "nvidia"
and model == "minimaxai/minimax-m3"
and "max_tokens" not in payloads
and "max_tokens" not in extra_body
):
payloads["max_tokens"] = 8192

if provider != "ollama":
return
if not self._ollama_disable_thinking_enabled():
return
Expand Down Expand Up @@ -543,7 +559,7 @@ async def _query(
custom_extra_body = self.provider_config.get("custom_extra_body", {})
if isinstance(custom_extra_body, dict):
extra_body.update(custom_extra_body)
self._apply_provider_specific_extra_body_overrides(extra_body)
self._apply_provider_specific_request_overrides(payloads, extra_body)

model = payloads.get("model", "").lower()

Expand Down Expand Up @@ -603,7 +619,7 @@ async def _query_stream(
to_del.append(key)
for key in to_del:
del payloads[key]
self._apply_provider_specific_extra_body_overrides(extra_body)
self._apply_provider_specific_request_overrides(payloads, extra_body)

self._sanitize_assistant_messages(payloads)

Expand Down
67 changes: 65 additions & 2 deletions tests/test_openai_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -1272,7 +1272,7 @@ async def fake_resolve_media_ref_to_base64_data(


@pytest.mark.asyncio
async def test_apply_provider_specific_extra_body_overrides_disables_ollama_thinking():
async def test_apply_provider_specific_request_overrides_disables_ollama_thinking():
provider = _make_provider(
{
"provider": "ollama",
Expand All @@ -1287,7 +1287,7 @@ async def test_apply_provider_specific_extra_body_overrides_disables_ollama_thin
"temperature": 0.2,
}

provider._apply_provider_specific_extra_body_overrides(extra_body)
provider._apply_provider_specific_request_overrides({}, extra_body)

assert extra_body["reasoning_effort"] == "none"
assert "reasoning" not in extra_body
Expand All @@ -1297,6 +1297,69 @@ async def test_apply_provider_specific_extra_body_overrides_disables_ollama_thin
await provider.terminate()


@pytest.mark.asyncio
async def test_provider_specific_request_overrides_sets_minimax_m3_max_tokens():
provider = _make_provider({"provider": "nvidia"})
try:
payloads = {"model": "minimaxai/minimax-m3"}
extra_body = {"temperature": 0.2}

provider._apply_provider_specific_request_overrides(payloads, extra_body)

assert payloads["max_tokens"] == 8192
assert extra_body == {"temperature": 0.2}
finally:
await provider.terminate()


@pytest.mark.asyncio
async def test_minimax_m3_max_tokens_preserves_custom_extra_body_value():
provider = _make_provider({"provider": "nvidia"})
try:
payloads = {"model": "minimaxai/minimax-m3"}
extra_body = {"max_tokens": 4096}

provider._apply_provider_specific_request_overrides(payloads, extra_body)

assert "max_tokens" not in payloads
assert extra_body["max_tokens"] == 4096
finally:
await provider.terminate()


@pytest.mark.asyncio
async def test_minimax_m3_max_tokens_preserves_standard_payload_value():
provider = _make_provider({"provider": "nvidia"})
try:
payloads = {
"model": "minimaxai/minimax-m3",
"max_tokens": 2048,
}
extra_body = {}

provider._apply_provider_specific_request_overrides(payloads, extra_body)

assert payloads["max_tokens"] == 2048
assert extra_body == {}
finally:
await provider.terminate()


@pytest.mark.asyncio
async def test_nvidia_request_does_not_set_max_tokens_for_other_models():
provider = _make_provider({"provider": "nvidia"})
try:
payloads = {"model": "nvidia/usdcode"}
extra_body = {}

provider._apply_provider_specific_request_overrides(payloads, extra_body)

assert "max_tokens" not in payloads
assert "max_tokens" not in extra_body
finally:
await provider.terminate()


@pytest.mark.asyncio
async def test_query_injects_reasoning_effort_none_for_ollama(monkeypatch):
provider = _make_provider(
Expand Down
Loading