Skip to content

Commit e411dbf

Browse files
committed
Fix couple of small issues.
1 parent 29adb4e commit e411dbf

4 files changed

Lines changed: 29 additions & 4 deletions

File tree

python_agent_harness/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ def _payload(
300300
"messages": msgs,
301301
"stream": stream,
302302
}
303+
if stream:
304+
payload["stream_options"] = {"include_usage": True}
303305
if tools:
304306
payload["tools"] = [t.to_api() for t in tools]
305307
if temperature is not None:

python_agent_harness/tools/bash.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def _execute(self, command: str, ctx: ToolContext) -> str | PendingToolResult:
6060
proc = subprocess.Popen(
6161
command,
6262
shell=True,
63+
stdin=subprocess.DEVNULL,
6364
stdout=subprocess.PIPE,
6465
stderr=subprocess.STDOUT,
6566
text=True,

tests/test_client.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,16 @@ def test_reasoning_content_streamed_and_captured(self):
7575

7676
def test_streaming_default_sends_stream_true(self):
7777
"""The default mode is streaming: the request body must carry
78-
stream=True unless the caller opts out."""
78+
stream=True plus stream_options requesting usage chunks."""
7979
fake_openai_server.reset_state()
8080
c = make_client()
8181
try:
8282
c.chat([Message(role="user", content="hi")])
8383
finally:
8484
c.close()
85-
self.assertIs(fake_openai_server.REQUEST_BODIES[-1]["stream"], True)
85+
body = fake_openai_server.REQUEST_BODIES[-1]
86+
self.assertIs(body["stream"], True)
87+
self.assertEqual(body["stream_options"], {"include_usage": True})
8688

8789

8890
class TestClientNonStreaming(unittest.TestCase):
@@ -114,13 +116,16 @@ def test_sync_chat_returns_full_response(self):
114116

115117
def test_sync_chat_sends_stream_false(self):
116118
"""Non-streaming mode must send stream=False in the payload
117-
(and not ask for text/event-stream)."""
119+
(and not ask for text/event-stream); stream_options must be
120+
absent since OpenAI-style backends reject it when not streaming."""
118121
c = make_client()
119122
try:
120123
c.chat([Message(role="user", content="hi")], stream=False)
121124
finally:
122125
c.close()
123-
self.assertIs(fake_openai_server.REQUEST_BODIES[-1]["stream"], False)
126+
body = fake_openai_server.REQUEST_BODIES[-1]
127+
self.assertIs(body["stream"], False)
128+
self.assertNotIn("stream_options", body)
124129

125130
def test_sync_chat_tool_calls_and_reasoning(self):
126131
"""stream=False parses content, reasoning_content, tool_calls

tests/test_tools_misc.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,23 @@ def test_communicate_failure_delivered_as_error(self):
333333
self.assertIsInstance(result, PendingToolResult)
334334
self.assertIn("Error: Bash failed", result.wait())
335335

336+
def test_popen_gets_devnull_stdin(self):
337+
"""Bash must not inherit the harness's stdin: commands that read
338+
stdin would steal keystrokes from the TUI."""
339+
import subprocess
340+
341+
from python_agent_harness.tools.bash import Bash
342+
343+
fake = mock.Mock()
344+
fake.communicate.return_value = ("", None)
345+
with mock.patch(
346+
"python_agent_harness.tools.bash.subprocess.Popen", return_value=fake
347+
) as popen:
348+
result = Bash().run({"command": "echo hi"}, ToolContext())
349+
result.wait()
350+
kwargs = popen.call_args.kwargs
351+
self.assertIs(kwargs["stdin"], subprocess.DEVNULL)
352+
336353

337354
if __name__ == "__main__":
338355
unittest.main()

0 commit comments

Comments
 (0)