ci/lint: ruff format pass and fix compact seed test typing

Apply pending ruff format; narrow AssistantChatMessage.content before
membership checks so basedpyright accepts Unset|None unions.
This commit is contained in:
2026-07-15 11:36:01 +08:00
parent 6e9e034de4
commit aaf7fe8e7b
18 changed files with 26 additions and 55 deletions
+1 -3
View File
@@ -52,9 +52,7 @@ def format_transcript(messages: Sequence[AnyChatMessage]) -> str:
if tool_calls is not UNSET and tool_calls: if tool_calls is not UNSET and tool_calls:
for call in tool_calls: for call in tool_calls:
if isinstance(call, AssistantFunctionToolCall): if isinstance(call, AssistantFunctionToolCall):
lines.append( lines.append(f"[assistant tool_call] {call.function.name}({call.function.arguments})")
f"[assistant tool_call] {call.function.name}({call.function.arguments})"
)
else: else:
lines.append(f"[assistant tool_call] custom id={call.id}") lines.append(f"[assistant tool_call] custom id={call.id}")
elif isinstance(msg, ToolChatMessage): elif isinstance(msg, ToolChatMessage):
+1 -3
View File
@@ -104,9 +104,7 @@ async def _execute_tool_calls(
*[_run_one_tool(tools, call, max_result_chars=max_result_chars) for call in tool_calls] *[_run_one_tool(tools, call, max_result_chars=max_result_chars) for call in tool_calls]
) )
else: else:
results = [ results = [await _run_one_tool(tools, call, max_result_chars=max_result_chars) for call in tool_calls]
await _run_one_tool(tools, call, max_result_chars=max_result_chars) for call in tool_calls
]
for tool_msg, err in results: for tool_msg, err in results:
if err is not None: if err is not None:
+1 -2
View File
@@ -90,8 +90,7 @@ async def _run_chat(
except ValueError as exc: except ValueError as exc:
raise click.ClickException(str(exc)) from exc raise click.ClickException(str(exc)) from exc
click.echo( click.echo(
f"resumed session {session_id} ({len(state.agent.messages)} messages) " f"resumed session {session_id} ({len(state.agent.messages)} messages) workspace={state.workspace}"
f"workspace={state.workspace}"
) )
elif new_session: elif new_session:
await state.new_session() await state.new_session()
-1
View File
@@ -92,4 +92,3 @@ async def render_events(events: AsyncIterator[AgentEvent]) -> None: # noqa: C90
if printed_text: if printed_text:
click.echo() click.echo()
click.echo() click.echo()
+1 -1
View File
@@ -43,7 +43,7 @@ def pause_task_cancel_for_prompt() -> Generator[None]:
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
_ = loop.remove_signal_handler(signal.SIGINT) _ = loop.remove_signal_handler(signal.SIGINT)
loop_handler_removed = True loop_handler_removed = True
except (RuntimeError, NotImplementedError, ValueError): except RuntimeError, NotImplementedError, ValueError:
loop_handler_removed = False loop_handler_removed = False
try: try:
+3 -3
View File
@@ -18,7 +18,7 @@ def _prompt_continue_limit_sync(reason: str) -> bool:
click.secho(f"[limit] {reason}", fg="yellow") click.secho(f"[limit] {reason}", fg="yellow")
try: try:
return bool(click.confirm("Raise limit and continue?", default=True)) return bool(click.confirm("Raise limit and continue?", default=True))
except (click.Abort, KeyboardInterrupt): except click.Abort, KeyboardInterrupt:
return False return False
@@ -39,7 +39,7 @@ def _prompt_confirm_tool_sync(name: str, args: Mapping[str, object], reason: str
click.secho(f"[confirm] tool {name!r}: {reason}", fg="yellow") click.secho(f"[confirm] tool {name!r}: {reason}", fg="yellow")
try: try:
return bool(click.confirm("Allow this tool call?", default=False)) return bool(click.confirm("Allow this tool call?", default=False))
except (click.Abort, KeyboardInterrupt): except click.Abort, KeyboardInterrupt:
return False return False
@@ -73,7 +73,7 @@ def _prompt_workspace_mismatch_sync(
default="k", default="k",
show_choices=True, show_choices=True,
) )
except (click.Abort, KeyboardInterrupt): except click.Abort, KeyboardInterrupt:
return "abort" return "abort"
key = str(raw).strip().lower() key = str(raw).strip().lower()
if key == "u": if key == "u":
+2 -7
View File
@@ -94,9 +94,7 @@ async def _cmd_sessions(state: ReplState) -> None:
for session in sessions: for session in sessions:
marker = "*" if session.sid == state.session_id else " " marker = "*" if session.sid == state.session_id else " "
ws = session.workspace or "(unbound)" ws = session.workspace or "(unbound)"
click.echo( click.echo(f"{marker} {session.sid}\t{session.name}\tworkspace={ws}\tupdated={session.updated_at}")
f"{marker} {session.sid}\t{session.name}\tworkspace={ws}\tupdated={session.updated_at}"
)
async def _cmd_new(state: ReplState, arg: str) -> None: async def _cmd_new(state: ReplState, arg: str) -> None:
@@ -126,10 +124,7 @@ async def _cmd_resume(state: ReplState, arg: str) -> None:
except ValueError as exc: except ValueError as exc:
click.echo(f"error: {exc}") click.echo(f"error: {exc}")
return return
click.echo( click.echo(f"resumed session {session_id} ({len(state.agent.messages)} messages) workspace={state.workspace}")
f"resumed session {session_id} ({len(state.agent.messages)} messages) "
f"workspace={state.workspace}"
)
async def _cmd_compact(state: ReplState, arg: str) -> None: async def _cmd_compact(state: ReplState, arg: str) -> None:
+3 -4
View File
@@ -57,14 +57,14 @@ async def run_cancellable[T](coro: Coroutine[object, object, T]) -> T:
def _reinstall() -> None: def _reinstall() -> None:
try: try:
loop.add_signal_handler(signal.SIGINT, _on_sigint) loop.add_signal_handler(signal.SIGINT, _on_sigint)
except (NotImplementedError, RuntimeError, ValueError): except NotImplementedError, RuntimeError, ValueError:
return return
try: try:
loop.add_signal_handler(signal.SIGINT, _on_sigint) loop.add_signal_handler(signal.SIGINT, _on_sigint)
installed = True installed = True
set_sigint_reinstall(_reinstall) set_sigint_reinstall(_reinstall)
except (NotImplementedError, RuntimeError, ValueError): except NotImplementedError, RuntimeError, ValueError:
installed = False installed = False
try: try:
@@ -115,8 +115,7 @@ def _echo_turn_usage(agent: ChatAgent) -> None:
async def _wait_for_retry(attempt: int, max_retries: int, wait: float) -> bool: async def _wait_for_retry(attempt: int, max_retries: int, wait: float) -> bool:
click.secho( click.secho(
f"auto-retry {attempt}/{max_retries} in {wait:g}s " f"auto-retry {attempt}/{max_retries} in {wait:g}s (Ctrl+C to cancel; then /retry later)",
f"(Ctrl+C to cancel; then /retry later)",
fg="yellow", fg="yellow",
) )
try: try:
+2 -6
View File
@@ -90,9 +90,7 @@ def copy_path(src: str, dst: str, *, overwrite: bool = False) -> str:
source, dest, root = pair source, dest, root = pair
if not source.exists() and not source.is_symlink(): if not source.exists() and not source.is_symlink():
return f"error: source does not exist: {src}" return f"error: source does not exist: {src}"
return _copy_or_move_validated( return _copy_or_move_validated(source, dest, root, src=src, dst=dst, overwrite=overwrite, action="copy")
source, dest, root, src=src, dst=dst, overwrite=overwrite, action="copy"
)
@tool @tool
@@ -106,9 +104,7 @@ def move_path(src: str, dst: str, *, overwrite: bool = False) -> str:
return f"error: source does not exist: {src}" return f"error: source does not exist: {src}"
if source == root: if source == root:
return "error: cannot move the workspace root" return "error: cannot move the workspace root"
return _copy_or_move_validated( return _copy_or_move_validated(source, dest, root, src=src, dst=dst, overwrite=overwrite, action="move")
source, dest, root, src=src, dst=dst, overwrite=overwrite, action="move"
)
def _delete_directory(target: Path, path: str, *, recursive: bool) -> str: def _delete_directory(target: Path, path: str, *, recursive: bool) -> str:
+2 -4
View File
@@ -41,7 +41,7 @@ def _collect_glob(
try: try:
resolved = candidate.resolve() resolved = candidate.resolve()
rel = resolved.relative_to(root) rel = resolved.relative_to(root)
except (OSError, ValueError): except OSError, ValueError:
continue continue
# Skip anything under (or itself) VCS / hidden path components. # Skip anything under (or itself) VCS / hidden path components.
if skip_hidden_dirs and _hidden_or_vcs(rel.parts): if skip_hidden_dirs and _hidden_or_vcs(rel.parts):
@@ -87,9 +87,7 @@ def glob_paths(
return resolved return resolved
root, base = resolved root, base = resolved
result = _collect_glob( result = _collect_glob(base, root, pattern, max_matches=max_matches, skip_hidden_dirs=skip_hidden_dirs)
base, root, pattern, max_matches=max_matches, skip_hidden_dirs=skip_hidden_dirs
)
if isinstance(result, str): if isinstance(result, str):
return result return result
matches, truncated = result matches, truncated = result
+2 -4
View File
@@ -210,7 +210,7 @@ class PtyManager:
ready, _, _ = select.select([session.master_fd], [], [], timeout) ready, _, _ = select.select([session.master_fd], [], [], timeout)
if ready: if ready:
data = os.read(session.master_fd, to_read) data = os.read(session.master_fd, to_read)
except (OSError, ValueError): except OSError, ValueError:
data = b"" data = b""
cls._poll_exit(session) cls._poll_exit(session)
if not data: if not data:
@@ -305,9 +305,7 @@ class PtyManager:
budget_exhausted=True, budget_exhausted=True,
) )
chunks, matched = cls._collect_chunks( chunks, matched = cls._collect_chunks(session, max_bytes=max_bytes, timeout=timeout, until=until)
session, max_bytes=max_bytes, timeout=timeout, until=until
)
data = b"".join(chunks).decode(errors="replace") data = b"".join(chunks).decode(errors="replace")
truncated = len(data) > max_bytes truncated = len(data) > max_bytes
if truncated: if truncated:
+1 -4
View File
@@ -18,10 +18,7 @@ def _backend_or_error() -> VcsBackend | str:
return f"error: {exc}" return f"error: {exc}"
backend = detect_vcs(root) backend = detect_vcs(root)
if backend is None: if backend is None:
return ( return "error: no supported VCS detected under workspace (currently: git; other systems can register detectors)"
"error: no supported VCS detected under workspace "
"(currently: git; other systems can register detectors)"
)
return backend return backend
+1 -3
View File
@@ -96,9 +96,7 @@ def test_compact_disabled_when_max_tokens_zero() -> None:
ToolChatMessage(content="x" * 500, tool_call_id="1"), ToolChatMessage(content="x" * 500, tool_call_id="1"),
] ]
out = compact_messages_for_request(messages, max_tokens=0) out = compact_messages_for_request(messages, max_tokens=0)
assert out[0] is messages[0] or ( assert out[0] is messages[0] or (isinstance(out[0], ToolChatMessage) and out[0].content == "x" * 500)
isinstance(out[0], ToolChatMessage) and out[0].content == "x" * 500
)
def test_measure_messages_tokens_calibrates_to_api_hint() -> None: def test_measure_messages_tokens_calibrates_to_api_hint() -> None:
+4 -2
View File
@@ -53,8 +53,10 @@ def test_build_compacted_seed_messages() -> None:
assert isinstance(seed[0], SystemChatMessage) assert isinstance(seed[0], SystemChatMessage)
assert seed[0].content == "sys" assert seed[0].content == "sys"
assert isinstance(seed[1], AssistantChatMessage) assert isinstance(seed[1], AssistantChatMessage)
assert "summary text" in seed[1].content content = seed[1].content
assert "session 3" in seed[1].content assert isinstance(content, str)
assert "summary text" in content
assert "session 3" in content
class SummaryClient: class SummaryClient:
-1
View File
@@ -617,7 +617,6 @@ async def test_retry_after_resume_orphan_user() -> None:
await store.close() await store.close()
async def test_chat_agent_system_prompt_prepended() -> None: async def test_chat_agent_system_prompt_prepended() -> None:
from plyngent.lmproto.openai_compatible.model import SystemChatMessage from plyngent.lmproto.openai_compatible.model import SystemChatMessage
+1 -3
View File
@@ -142,9 +142,7 @@ async def test_rounds(state: ReplState) -> None:
assert state.agent.max_rounds == 40 assert state.agent.max_rounds == 40
async def test_status_shows_context_tokens( async def test_status_shows_context_tokens(state: ReplState, capsys: pytest.CaptureFixture[str]) -> None:
state: ReplState, capsys: pytest.CaptureFixture[str]
) -> None:
from plyngent.agent.usage import TokenUsage from plyngent.agent.usage import TokenUsage
from plyngent.lmproto.openai_compatible.model import UserChatMessage from plyngent.lmproto.openai_compatible.model import UserChatMessage
-1
View File
@@ -41,4 +41,3 @@ max_context_tokens = 5000
assert store.agent_config.confirm_destructive is False assert store.agent_config.confirm_destructive is False
assert store.agent_config.path_denylist == ["/secrets/", ".ssh/"] assert store.agent_config.path_denylist == ["/secrets/", ".ssh/"]
assert store.agent_config.max_context_tokens == 5000 assert store.agent_config.max_context_tokens == 5000
+1 -3
View File
@@ -13,9 +13,7 @@ def test_classify_delete_and_move() -> None:
def test_classify_copy_overwrite_only() -> None: def test_classify_copy_overwrite_only() -> None:
assert classify_danger("copy_path", {"src": "a", "dst": "b"}) is None assert classify_danger("copy_path", {"src": "a", "dst": "b"}) is None
assert "overwrite" in ( assert "overwrite" in (classify_danger("copy_path", {"src": "a", "dst": "b", "overwrite": True}) or "")
classify_danger("copy_path", {"src": "a", "dst": "b", "overwrite": True}) or ""
)
def test_classify_write_file_overwrite(workspace: object) -> None: def test_classify_write_file_overwrite(workspace: object) -> None: