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
16 changes: 11 additions & 5 deletions responses_api_agents/remote_agent/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,17 @@ async def responses(

# Execute only unpaired calls: a call the service already answered itself (matching
# function_call_output in the same response) is its own internal-tool record and
# passes through into the trajectory untouched.
answered_call_ids = {o.call_id for o in output if o.type == "function_call_output"}
all_fn_calls: List[NeMoGymResponseFunctionToolCall] = [
o for o in output if o.type == "function_call" and o.call_id not in answered_call_ids
]
# passes through into the trajectory untouched. Duplicate call_ids collapse to the
# last occurrence (call_id is unique by contract).
pending_calls: Dict[str, NeMoGymResponseFunctionToolCall] = {}
answered_call_ids = set()
for item in output:
if item.type == "function_call_output":
answered_call_ids.add(item.call_id)
pending_calls.pop(item.call_id, None)
elif item.type == "function_call" and item.call_id not in answered_call_ids:
pending_calls[item.call_id] = item
all_fn_calls: List[NeMoGymResponseFunctionToolCall] = list(pending_calls.values())
all_output_messages: List[NeMoGymResponseOutputMessage] = [
o for o in output if o.type == "message" and o.role == "assistant"
]
Expand Down
20 changes: 20 additions & 0 deletions responses_api_agents/remote_agent/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,26 @@ async def test_paired_calls_pass_through_unexecuted(self, monkeypatch: pytest.Mo
types = [o["type"] for o in dumped["response"]["output"]]
assert types.count("function_call") == 2 and types.count("function_call_output") == 2

async def test_duplicate_call_ids_execute_once_with_last_arguments(self, monkeypatch: pytest.MonkeyPatch) -> None:
# call_id is unique by contract; a malformed duplicate collapses to the last occurrence.
service = scripted_service(
traj(
[
fn_call("dup", "increment_counter", '{"count": 1}'),
fn_call("dup", "increment_counter", '{"count": 3}'),
]
),
traj([msg("done")]),
)
agent, _, server_client = make_wired_agent(monkeypatch, service, tool_handler=self.counter_tool_handler)

result = await agent.run(make_request(), RemoteAgentRunRequest.model_validate(make_row(tools=_COUNTER_TOOLS)))

assert NG_FAILURE_CLASS_KEY not in result.model_dump()
tool_calls = [c for c in server_client.calls if c["url_path"] == "/increment_counter"]
assert len(tool_calls) == 1
assert tool_calls[0]["json"] == {"count": 3}

async def test_unknown_tool_error_fed_back_not_raised(self, monkeypatch: pytest.MonkeyPatch) -> None:
service = scripted_service(
traj([fn_call("c1", "web_search", "{}")]), # unpaired ask for a tool the env doesn't serve
Expand Down
Loading