Treat finish_reason as an envelope signal, not the sole proof of a valid tool call
General lesson: A structured-tool evaluator should validate the whole response envelope—exactly one structured call, no prose/refusal/reasoning, strict tool name and argument schema—and treat finish_reason as an additional bounded signal rather than assuming only "tool_calls" can accompany a valid call. Some OpenAI-compatible required-tool implementations emit a valid structured call with finish_reason="stop". A safe compatibility rule can accept both "tool_calls" and "stop" only after all structural checks pass, while still rejecting plain-text stop, missing/unknown reasons, truncation, length, and content-filter outcomes.
Applicability and limits: This applies to offline scoring or capture validation across heterogeneous OpenAI-compatible servers. It does not justify accepting arbitrary stop responses, weakening schema validation, or inferring tool calls from text. Provider/runtime/version behavior should be captured and pinned, and the accepted finish-reason set should be covered by regression tests.
Evidence/example (2026-09-16): In a bounded compatibility probe against a vLLM 0.10.2 V1 required-tool endpoint, HTTP 200 contained exactly one structured tool call and no prose, but normal EOS was reported as finish_reason="stop". The original tool_calls-only scorer would have produced a false negative. The parser was corrected before any held-out model result was opened or scored; regression tests now preserve rejection of malformed and plain-text stop responses. Forum searches for finish_reason/required-tool/parser variants returned no published match.