fix: explain explicit auto migration and verify probe cleanup
This commit is contained in:
@@ -696,6 +696,13 @@ def _tier_unsupported(
|
|||||||
if fallback == "nearest" or effort is Effort.AUTO
|
if fallback == "nearest" or effort is Effort.AUTO
|
||||||
else ";若希望自动落到最近的档,请配 EFFORT_FALLBACK=nearest"
|
else ";若希望自动落到最近的档,请配 EFFORT_FALLBACK=nearest"
|
||||||
)
|
)
|
||||||
|
if effort is Effort.AUTO:
|
||||||
|
example = capability.cheapest_effort or Effort.NONE
|
||||||
|
hint = (
|
||||||
|
";请显式选择清单中的档位,例如 "
|
||||||
|
f"{{SCOPE}}__{{PROVIDER}}__{{N}}__REASONING_EFFORT={example.value},"
|
||||||
|
f"或调用时传 reasoning_effort=Effort.{example.name};库不会自动应用此选择"
|
||||||
|
)
|
||||||
return f"{head}{body}{hint}"
|
return f"{head}{body}{hint}"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1316,3 +1316,27 @@ class TestManagedReasoningAdmission:
|
|||||||
assert (await client._limiter_backend.source_stats(source.name)).inflight == 0
|
assert (await client._limiter_backend.source_stats(source.name)).inflight == 0
|
||||||
finally:
|
finally:
|
||||||
await client._transport.aclose()
|
await client._transport.aclose()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_managed_conflict_returns_half_open_probe_and_permit():
|
||||||
|
"""本地拒绝没有上游响应,不计故障且必须归还半开探针。"""
|
||||||
|
from tests.contracts.conftest import FakeClock
|
||||||
|
|
||||||
|
clock = FakeClock()
|
||||||
|
source = _source(
|
||||||
|
provider="openai", reasoning_effort="high", extra_body={"reasoning_effort": "high"}
|
||||||
|
)
|
||||||
|
gate = InMemoryGate(config=BreakerConfig(1, 1, 10), now=clock)
|
||||||
|
entry = await gate.try_enter(source.name, "setup")
|
||||||
|
await gate.record_failure(entry, "source_dead", True)
|
||||||
|
clock.advance(2)
|
||||||
|
client = _client(sources=[source], breaker=gate)
|
||||||
|
try:
|
||||||
|
with pytest.raises(RequestRejectedError, match="冲突"):
|
||||||
|
await client.chat([])
|
||||||
|
assert (await client._limiter_backend.source_stats(source.name)).inflight == 0
|
||||||
|
next_entry = await gate.try_enter(source.name, "next")
|
||||||
|
assert next_entry.allowed and next_entry.is_probe
|
||||||
|
await gate.release_probe(next_entry)
|
||||||
|
finally:
|
||||||
|
await client._transport.aclose()
|
||||||
|
|||||||
@@ -911,3 +911,14 @@ class TestThinkingRawOwnership:
|
|||||||
wire = ThinkingWire(off=None, on_base={"output_config": {"enabled": True}}, effort_key=None)
|
wire = ThinkingWire(off=None, on_base={"output_config": {"enabled": True}}, effort_key=None)
|
||||||
with pytest.raises(ThinkingUnsupportedError):
|
with pytest.raises(ThinkingUnsupportedError):
|
||||||
validate_thinking_raw(raw, effort=Effort.AUTO, wire=wire, origin="test")
|
validate_thinking_raw(raw, effort=Effort.AUTO, wire=wire, origin="test")
|
||||||
|
|
||||||
|
|
||||||
|
def test_auto_rejection_explains_how_to_choose_explicitly():
|
||||||
|
"""可执行配置是指路,不由库自动应用其建议。"""
|
||||||
|
with pytest.raises(ThinkingUnsupportedError) as error:
|
||||||
|
resolve_thinking(
|
||||||
|
get_provider("minimax"), get_capability("MiniMax-M3"), Effort.AUTO, model="MiniMax-M3"
|
||||||
|
)
|
||||||
|
assert "REASONING_EFFORT=" in str(error.value)
|
||||||
|
assert "reasoning_effort=Effort." in str(error.value)
|
||||||
|
assert "EFFORT_FALLBACK=nearest" not in str(error.value)
|
||||||
|
|||||||
Reference in New Issue
Block a user