mirror of
https://github.com/khodges42/nightShift.git
synced 2026-06-14 18:18:36 +00:00
Fix a windows bug for i/o from ollama
This commit is contained in:
parent
360f449738
commit
169c7aacae
|
|
@ -149,6 +149,8 @@ class AgentExecutor:
|
||||||
input=prompt,
|
input=prompt,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
timeout=self.timeout_seconds,
|
timeout=self.timeout_seconds,
|
||||||
)
|
)
|
||||||
duration = time.monotonic() - started
|
duration = time.monotonic() - started
|
||||||
|
|
@ -157,8 +159,8 @@ class AgentExecutor:
|
||||||
command=agent.command,
|
command=agent.command,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
exit_code=completed.returncode,
|
exit_code=completed.returncode,
|
||||||
stdout=completed.stdout,
|
stdout=_coerce_output(completed.stdout),
|
||||||
stderr=completed.stderr,
|
stderr=_coerce_output(completed.stderr),
|
||||||
duration_seconds=duration,
|
duration_seconds=duration,
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired as exc:
|
except subprocess.TimeoutExpired as exc:
|
||||||
|
|
@ -186,6 +188,8 @@ class AgentExecutor:
|
||||||
input=prompt,
|
input=prompt,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
timeout=self.timeout_seconds,
|
timeout=self.timeout_seconds,
|
||||||
)
|
)
|
||||||
duration = time.monotonic() - started
|
duration = time.monotonic() - started
|
||||||
|
|
@ -194,8 +198,8 @@ class AgentExecutor:
|
||||||
command=command,
|
command=command,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
exit_code=completed.returncode,
|
exit_code=completed.returncode,
|
||||||
stdout=completed.stdout,
|
stdout=_coerce_output(completed.stdout),
|
||||||
stderr=completed.stderr,
|
stderr=_coerce_output(completed.stderr),
|
||||||
duration_seconds=duration,
|
duration_seconds=duration,
|
||||||
)
|
)
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
|
|
@ -323,6 +327,9 @@ def parse_review_output(output: str) -> tuple[StageStatus, str, str | None, str
|
||||||
|
|
||||||
|
|
||||||
def format_agent_invocation(stage_id: str, invocation: AgentInvocation) -> str:
|
def format_agent_invocation(stage_id: str, invocation: AgentInvocation) -> str:
|
||||||
|
stdout = _coerce_output(invocation.stdout)
|
||||||
|
stderr = _coerce_output(invocation.stderr)
|
||||||
|
prompt = _coerce_output(invocation.prompt)
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
f"# Agent Output: {stage_id}",
|
f"# Agent Output: {stage_id}",
|
||||||
|
|
@ -336,19 +343,19 @@ def format_agent_invocation(stage_id: str, invocation: AgentInvocation) -> str:
|
||||||
"## stdout",
|
"## stdout",
|
||||||
"",
|
"",
|
||||||
"```text",
|
"```text",
|
||||||
invocation.stdout.rstrip(),
|
stdout.rstrip(),
|
||||||
"```",
|
"```",
|
||||||
"",
|
"",
|
||||||
"## stderr",
|
"## stderr",
|
||||||
"",
|
"",
|
||||||
"```text",
|
"```text",
|
||||||
invocation.stderr.rstrip(),
|
stderr.rstrip(),
|
||||||
"```",
|
"```",
|
||||||
"",
|
"",
|
||||||
"## Prompt",
|
"## Prompt",
|
||||||
"",
|
"",
|
||||||
"```markdown",
|
"```markdown",
|
||||||
invocation.prompt.rstrip(),
|
prompt.rstrip(),
|
||||||
"```",
|
"```",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,8 @@ class CommandExecutor:
|
||||||
shell=shell,
|
shell=shell,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
@ -132,8 +134,8 @@ class CommandExecutor:
|
||||||
return CommandRun(
|
return CommandRun(
|
||||||
command=normalized,
|
command=normalized,
|
||||||
exit_code=completed.returncode,
|
exit_code=completed.returncode,
|
||||||
stdout=completed.stdout,
|
stdout=_coerce_output(completed.stdout),
|
||||||
stderr=completed.stderr,
|
stderr=_coerce_output(completed.stderr),
|
||||||
duration_seconds=duration,
|
duration_seconds=duration,
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired as exc:
|
except subprocess.TimeoutExpired as exc:
|
||||||
|
|
@ -151,6 +153,8 @@ class CommandExecutor:
|
||||||
def format_command_runs(stage_id: str, runs: list[CommandRun]) -> str:
|
def format_command_runs(stage_id: str, runs: list[CommandRun]) -> str:
|
||||||
lines = [f"# Command Output: {stage_id}", ""]
|
lines = [f"# Command Output: {stage_id}", ""]
|
||||||
for index, run in enumerate(runs, start=1):
|
for index, run in enumerate(runs, start=1):
|
||||||
|
stdout = _coerce_output(run.stdout)
|
||||||
|
stderr = _coerce_output(run.stderr)
|
||||||
lines.extend(
|
lines.extend(
|
||||||
[
|
[
|
||||||
f"## Command {index}",
|
f"## Command {index}",
|
||||||
|
|
@ -163,13 +167,13 @@ def format_command_runs(stage_id: str, runs: list[CommandRun]) -> str:
|
||||||
"### stdout",
|
"### stdout",
|
||||||
"",
|
"",
|
||||||
"```text",
|
"```text",
|
||||||
run.stdout.rstrip(),
|
stdout.rstrip(),
|
||||||
"```",
|
"```",
|
||||||
"",
|
"",
|
||||||
"### stderr",
|
"### stderr",
|
||||||
"",
|
"",
|
||||||
"```text",
|
"```text",
|
||||||
run.stderr.rstrip(),
|
stderr.rstrip(),
|
||||||
"```",
|
"```",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,18 @@ def run_git(project_root: Path, args: list[str], timeout_seconds: int = 15) -> G
|
||||||
cwd=project_root,
|
cwd=project_root,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
timeout=timeout_seconds,
|
timeout=timeout_seconds,
|
||||||
)
|
)
|
||||||
except (OSError, subprocess.TimeoutExpired) as exc:
|
except (OSError, subprocess.TimeoutExpired) as exc:
|
||||||
return GitCommandResult(False, -1, "", str(exc))
|
return GitCommandResult(False, -1, "", str(exc))
|
||||||
return GitCommandResult(completed.returncode == 0, completed.returncode, completed.stdout, completed.stderr)
|
return GitCommandResult(
|
||||||
|
completed.returncode == 0,
|
||||||
|
completed.returncode,
|
||||||
|
completed.stdout or "",
|
||||||
|
completed.stderr or "",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_git_status(project_root: Path) -> GitCommandResult:
|
def get_git_status(project_root: Path) -> GitCommandResult:
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,8 @@ def collect_modified_files(project_root: Path) -> list[str]:
|
||||||
shell=True,
|
shell=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
timeout=10,
|
timeout=10,
|
||||||
)
|
)
|
||||||
except (OSError, subprocess.TimeoutExpired):
|
except (OSError, subprocess.TimeoutExpired):
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from nightshift.agents import AgentExecutor, build_prompt_bundle, parse_review_output
|
from nightshift.agents import AgentExecutor, build_prompt_bundle, parse_review_output
|
||||||
|
from nightshift.agents import AgentInvocation, format_agent_invocation
|
||||||
from nightshift.artifacts import ArtifactStore
|
from nightshift.artifacts import ArtifactStore
|
||||||
from nightshift.config import AgentConfig, StageConfig
|
from nightshift.config import AgentConfig, StageConfig
|
||||||
from nightshift.tasks import parse_tasks
|
from nightshift.tasks import parse_tasks
|
||||||
|
|
@ -131,6 +132,22 @@ class AgentExecutorTests(unittest.TestCase):
|
||||||
output = (root / result.output_path).read_text(encoding="utf-8")
|
output = (root / result.output_path).read_text(encoding="utf-8")
|
||||||
self.assertIn("ollama run tiny-model", output)
|
self.assertIn("ollama run tiny-model", output)
|
||||||
|
|
||||||
|
def test_agent_artifact_format_tolerates_missing_streams(self) -> None:
|
||||||
|
invocation = AgentInvocation(
|
||||||
|
agent_id="planner",
|
||||||
|
command="ollama run model",
|
||||||
|
prompt="prompt",
|
||||||
|
exit_code=0,
|
||||||
|
stdout=None, # type: ignore[arg-type]
|
||||||
|
stderr=None, # type: ignore[arg-type]
|
||||||
|
duration_seconds=0.1,
|
||||||
|
)
|
||||||
|
|
||||||
|
output = format_agent_invocation("plan", invocation)
|
||||||
|
|
||||||
|
self.assertIn("Agent: `planner`", output)
|
||||||
|
self.assertIn("## stderr", output)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import unittest
|
||||||
|
|
||||||
from nightshift.artifacts import ArtifactStore
|
from nightshift.artifacts import ArtifactStore
|
||||||
from nightshift.commands import CommandExecutor
|
from nightshift.commands import CommandExecutor
|
||||||
|
from nightshift.commands import CommandRun, format_command_runs
|
||||||
from nightshift.config import SafetyConfig, StageConfig
|
from nightshift.config import SafetyConfig, StageConfig
|
||||||
from nightshift.errors import CommandError
|
from nightshift.errors import CommandError
|
||||||
|
|
||||||
|
|
@ -150,6 +151,23 @@ class CommandExecutorTests(unittest.TestCase):
|
||||||
output = (root / result.output_path).read_text(encoding="utf-8")
|
output = (root / result.output_path).read_text(encoding="utf-8")
|
||||||
self.assertIn("work", output)
|
self.assertIn("work", output)
|
||||||
|
|
||||||
|
def test_command_artifact_format_tolerates_missing_streams(self) -> None:
|
||||||
|
output = format_command_runs(
|
||||||
|
"test",
|
||||||
|
[
|
||||||
|
CommandRun(
|
||||||
|
command="cmd",
|
||||||
|
exit_code=0,
|
||||||
|
stdout=None, # type: ignore[arg-type]
|
||||||
|
stderr=None, # type: ignore[arg-type]
|
||||||
|
duration_seconds=0.1,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn("Command: `cmd`", output)
|
||||||
|
self.assertIn("### stderr", output)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user