nisse-review: a stop ends a review in progress; quieter repo failures
SIGTERM used to stop only new rounds: a review in progress ran on until systemd killed it 90s later. Now the scheduler's stop sets the driver's cancel flag, so the run ends at its next turn, and a model call in flight is broken off (Interruptible, raising Stopped from the signal handler, only ever inside a model call). The item goes back to ready, with nothing drafted posted and no failure counted; replies already posted carry their markers and are not repeated. intake.repo_failed is logged when a repository starts failing or the reason changes, and intake.repo_ok when it lists again, not every round. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
This commit is contained in:
1 parent
52d30ccab9
commit
a85e855fc2
8 files changed
+287
-10
No files matched your search
@@ -0,0 +1,51 @@
|
||||
"""A model call that a stop can break off (L2).
|
||||
|
||||
A ``Cancel`` takes effect at the next tool boundary, but one model call can
|
||||
run for many minutes (``request_timeout_s``), longer than a service manager
|
||||
waits before it kills the process. ``Interruptible`` wraps the client and
|
||||
knows when a call is in flight; ``interrupt``, called from the signal
|
||||
handler, raises ``Stopped`` inside that call so it ends at once.
|
||||
|
||||
Only a model call is ever broken off this way, never a post or a write:
|
||||
outside a call, ``interrupt`` does nothing and the run ends at its next
|
||||
boundary instead. Python runs signal handlers on the main thread, so this
|
||||
works when the calls are made there.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from nisse_core.log.canonical import CanonicalEvent, StoredEvent
|
||||
from nisse_core.models.llm import LLMClient
|
||||
|
||||
|
||||
class Stopped(BaseException):
|
||||
"""A model call broken off by a stop. A ``BaseException``, so no handler
|
||||
meant for failures (``except Exception``, ``except LLMError``) takes it
|
||||
for one."""
|
||||
|
||||
|
||||
class Interruptible:
|
||||
def __init__(self, client: LLMClient) -> None:
|
||||
self._client = client
|
||||
self._calling = False
|
||||
|
||||
def complete(
|
||||
self,
|
||||
events: list[StoredEvent],
|
||||
tools: list[dict[str, Any]] | None,
|
||||
model: str,
|
||||
system: str | None = None,
|
||||
) -> list[CanonicalEvent]:
|
||||
self._calling = True
|
||||
try:
|
||||
return self._client.complete(events, tools, model, system)
|
||||
finally:
|
||||
self._calling = False
|
||||
|
||||
def interrupt(self) -> None:
|
||||
"""From a signal handler: breaks off the call in flight, if any."""
|
||||
if self._calling:
|
||||
raise Stopped("a stop broke off the model call")
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(self._client, name)
|
||||
Reference in new issue
Block a user