mirror of
https://github.com/NCBM/plyngent.git
synced 2026-07-23 05:55:16 +08:00
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:
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user