mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix(openai): tool_call delta 重复携带 id/type,修复严格客户端兼容性
部分 OpenAI 兼容客户端要求每个 tool_call delta chunk 都包含 id 和 type 字段,否则会将后续 delta 视为无效。通过 block_to_tool_id 映射在 ContentBlockStart 时记录 tool_id,确保后续 ToolCallDelta 能正确回填。
This commit is contained in:
@@ -796,6 +796,8 @@ class OpenAINormalizer(FormatNormalizer):
|
|||||||
tool_id = event.tool_id or ""
|
tool_id = event.tool_id or ""
|
||||||
tool_name = event.tool_name or ""
|
tool_name = event.tool_name or ""
|
||||||
tool_index = self._ensure_tool_call_index(ss, tool_id, event.block_index)
|
tool_index = self._ensure_tool_call_index(ss, tool_id, event.block_index)
|
||||||
|
if tool_id:
|
||||||
|
ss.setdefault("block_to_tool_id", {})[int(event.block_index)] = str(tool_id)
|
||||||
out.append(
|
out.append(
|
||||||
base_chunk(
|
base_chunk(
|
||||||
{
|
{
|
||||||
@@ -862,12 +864,22 @@ class OpenAINormalizer(FormatNormalizer):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
if isinstance(event, ToolCallDeltaEvent):
|
if isinstance(event, ToolCallDeltaEvent):
|
||||||
tool_index = self._ensure_tool_call_index(ss, event.tool_id, event.block_index)
|
tool_id = str(event.tool_id or "")
|
||||||
# 后续 delta 只需 index + function.arguments;id/type 仅在 ContentBlockStartEvent 首次发送
|
block_to_tool_id = ss.setdefault("block_to_tool_id", {})
|
||||||
|
if not tool_id:
|
||||||
|
tool_id = str(block_to_tool_id.get(int(event.block_index)) or "")
|
||||||
|
else:
|
||||||
|
block_to_tool_id[int(event.block_index)] = tool_id
|
||||||
|
|
||||||
|
tool_index = self._ensure_tool_call_index(ss, tool_id, event.block_index)
|
||||||
|
# 对严格客户端重复携带 id/type,避免它们把后续 delta 视为无效 tool_call。
|
||||||
tc_delta: dict[str, Any] = {
|
tc_delta: dict[str, Any] = {
|
||||||
"index": tool_index,
|
"index": tool_index,
|
||||||
|
"type": "function",
|
||||||
"function": {"arguments": event.input_delta},
|
"function": {"arguments": event.input_delta},
|
||||||
}
|
}
|
||||||
|
if tool_id:
|
||||||
|
tc_delta["id"] = tool_id
|
||||||
out.append(base_chunk({"tool_calls": [tc_delta]}))
|
out.append(base_chunk({"tool_calls": [tc_delta]}))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|||||||
@@ -857,15 +857,21 @@ def test_tool_call_stream_index_stable_across_deltas() -> None:
|
|||||||
|
|
||||||
# 收集所有 tool_calls chunk
|
# 收集所有 tool_calls chunk
|
||||||
tc_indices: list[int] = []
|
tc_indices: list[int] = []
|
||||||
|
tc_ids: list[str] = []
|
||||||
for event in all_events:
|
for event in all_events:
|
||||||
for choice in event.get("choices", []):
|
for choice in event.get("choices", []):
|
||||||
tcs = choice.get("delta", {}).get("tool_calls")
|
tcs = choice.get("delta", {}).get("tool_calls")
|
||||||
if isinstance(tcs, list):
|
if isinstance(tcs, list):
|
||||||
for tc in tcs:
|
for tc in tcs:
|
||||||
tc_indices.append(tc["index"])
|
tc_indices.append(tc["index"])
|
||||||
|
tc_ids.append(str(tc.get("id") or ""))
|
||||||
|
|
||||||
assert len(tc_indices) >= 2, f"expected at least 2 tool_call chunks, got {len(tc_indices)}"
|
assert len(tc_indices) >= 2, f"expected at least 2 tool_call chunks, got {len(tc_indices)}"
|
||||||
# 同一个 tool call 的所有 chunk 必须使用相同的 index
|
# 同一个 tool call 的所有 chunk 必须使用相同的 index
|
||||||
assert all(
|
assert all(
|
||||||
idx == tc_indices[0] for idx in tc_indices
|
idx == tc_indices[0] for idx in tc_indices
|
||||||
), f"tool_call index should be stable, got: {tc_indices}"
|
), f"tool_call index should be stable, got: {tc_indices}"
|
||||||
|
assert all(tc_id == "fc_001" for tc_id in tc_ids), (
|
||||||
|
"tool_call id should be repeated on every delta for strict OpenAI-compatible clients, "
|
||||||
|
f"got: {tc_ids}"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user