refactor(agent/side_question,review_*,thinking_timeout_guidance): flatten call sites, drop section banners
This commit is contained in:
@@ -106,8 +106,7 @@ def build_review_task(snapshot: List[Dict[str, str]], user_prompt: str = "", loa
|
||||
"--- Recent conversation (oldest first) ---",
|
||||
]
|
||||
for message in snapshot:
|
||||
label = "USER" if message["role"] == "user" else "PRIMARY AGENT"
|
||||
lines += [f"[{label}]", message["text"], ""]
|
||||
lines += [f"[{'USER' if message['role'] == 'user' else 'PRIMARY AGENT'}]", message["text"], ""]
|
||||
lines.append("--- End of conversation excerpt ---")
|
||||
if loaded_skills:
|
||||
skill_list = ", ".join(loaded_skills)
|
||||
@@ -135,12 +134,11 @@ def _load_review_credentials_cfg() -> Optional[Dict[str, Any]]:
|
||||
and no model/base_url) so the reviewer inherits the parent's credentials."""
|
||||
try:
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
review = (load_config_readonly().get("auxiliary") or {}).get("review") or {}
|
||||
if not isinstance(review, dict):
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
if not isinstance(review, dict):
|
||||
return None
|
||||
|
||||
cfg = {k: str(review.get(k) or "").strip() for k in ("provider", "model", "base_url", "api_key", "api_mode")}
|
||||
if cfg["provider"].lower() == "auto":
|
||||
@@ -156,17 +154,13 @@ def start_review(parent_agent, messages: List[Dict[str, Any]], user_prompt: str
|
||||
when there is nothing to review or the dispatch is rejected/errored."""
|
||||
if parent_agent is None:
|
||||
raise ValueError("No active agent — send a message first.")
|
||||
|
||||
snapshot = snapshot_recent_messages(messages)
|
||||
if not snapshot:
|
||||
raise ValueError("Nothing to review yet — the conversation is empty.")
|
||||
|
||||
loaded_skills = collect_parent_loaded_skills(parent_agent, messages)
|
||||
goal, context = build_review_task(snapshot, user_prompt, loaded_skills)
|
||||
goal, context = build_review_task(snapshot, user_prompt, collect_parent_loaded_skills(parent_agent, messages))
|
||||
credentials_cfg = _load_review_credentials_cfg()
|
||||
|
||||
from tools.delegate_tool import delegate_task
|
||||
|
||||
raw = delegate_task(goal=goal, context=context, background=True, parent_agent=parent_agent, credentials_cfg=credentials_cfg)
|
||||
try:
|
||||
result = json.loads(raw)
|
||||
|
||||
@@ -80,8 +80,6 @@ class ReviewIdleQueue:
|
||||
self._now: Callable[[], float] = time.monotonic
|
||||
self._server_idle: Callable[[], bool] = _managed_server_idle
|
||||
|
||||
# ── turn liveness (this process) ────────────────────────────
|
||||
|
||||
def note_turn_started(self) -> None:
|
||||
with self._lock:
|
||||
self._live_turns += 1
|
||||
@@ -94,8 +92,6 @@ class ReviewIdleQueue:
|
||||
self._quiet_since = self._now()
|
||||
self._wake.set()
|
||||
|
||||
# ── queue ────────────────────────────────────────────────────
|
||||
|
||||
def enqueue(self, agent: Any, session_key: str, kwargs: Dict[str, Any]) -> None:
|
||||
"""Add (or replace — newest snapshot wins) a session's pending review, keeping the ORIGINAL
|
||||
enqueue time on coalesce so a busy session cannot push its age-out forever."""
|
||||
@@ -111,8 +107,6 @@ class ReviewIdleQueue:
|
||||
with self._lock:
|
||||
return len(self._pending)
|
||||
|
||||
# ── dispatcher ───────────────────────────────────────────────
|
||||
|
||||
def _ensure_thread(self) -> None:
|
||||
with self._lock:
|
||||
if self._thread is None or not self._thread.is_alive():
|
||||
@@ -193,8 +187,7 @@ def _managed_server_idle() -> bool:
|
||||
return True
|
||||
|
||||
def _get(path: str) -> Any:
|
||||
req = urllib.request.Request(f"{base}{path}", headers=headers)
|
||||
with urllib.request.urlopen(req, timeout=3) as r:
|
||||
with urllib.request.urlopen(urllib.request.Request(f"{base}{path}", headers=headers), timeout=3) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
loaded = [m["id"] for m in _get("/models").get("data", [])
|
||||
|
||||
@@ -59,10 +59,7 @@ def trim_snapshot_for_fork(history: Optional[List[Dict[str, Any]]]) -> List[Dict
|
||||
return msgs
|
||||
|
||||
|
||||
def render_history_for_side_question(
|
||||
history: Optional[List[Dict[str, Any]]],
|
||||
char_budget: int = _TRANSCRIPT_CHAR_BUDGET,
|
||||
) -> str:
|
||||
def render_history_for_side_question(history: Optional[List[Dict[str, Any]]], char_budget: int = _TRANSCRIPT_CHAR_BUDGET) -> str:
|
||||
"""Plain-text transcript for the fallback path: newest-biased fit to ``char_budget``,
|
||||
tool calls summarized by name, tool results truncated, system prompt skipped."""
|
||||
lines: List[str] = []
|
||||
@@ -95,11 +92,9 @@ def _side_question_task_config() -> Dict[str, Any]:
|
||||
"""Return ``auxiliary.side_question`` from config (or ``{}``)."""
|
||||
try:
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
cfg = load_config_readonly()
|
||||
aux = load_config_readonly().get("auxiliary")
|
||||
except Exception:
|
||||
return {}
|
||||
aux = cfg.get("auxiliary")
|
||||
task = aux.get(SIDE_QUESTION_TASK) if isinstance(aux, dict) else None
|
||||
return task if isinstance(task, dict) else {}
|
||||
|
||||
@@ -118,18 +113,12 @@ def _answer_via_fork(parent_agent: Any, question: str, history: Optional[List[Di
|
||||
fork, _rt, routed = build_cache_parity_fork(parent_agent, _side_question_task_config(),
|
||||
max_iterations=_FORK_MAX_ITERATIONS, write_origin="side_question")
|
||||
try:
|
||||
set_thread_tool_whitelist(
|
||||
set(),
|
||||
deny_msg_fmt=(
|
||||
"Side question (/btw) denied tool call: {tool_name}. "
|
||||
"Tools are disabled here — answer directly from the conversation context."
|
||||
),
|
||||
)
|
||||
set_thread_tool_whitelist(set(), deny_msg_fmt=(
|
||||
"Side question (/btw) denied tool call: {tool_name}. "
|
||||
"Tools are disabled here — answer directly from the conversation context."))
|
||||
snapshot = trim_snapshot_for_fork(history)
|
||||
result = fork.run_conversation(
|
||||
user_message=f"{_FORK_PROMPT}\n\nSide question: {question}",
|
||||
conversation_history=_digest_history(snapshot) if routed else snapshot,
|
||||
)
|
||||
result = fork.run_conversation(user_message=f"{_FORK_PROMPT}\n\nSide question: {question}",
|
||||
conversation_history=_digest_history(snapshot) if routed else snapshot)
|
||||
answer = (result or {}).get("final_response", "") or ""
|
||||
if not answer and result and result.get("error"):
|
||||
raise RuntimeError(str(result["error"]))
|
||||
@@ -137,11 +126,8 @@ def _answer_via_fork(parent_agent: Any, question: str, history: Optional[List[Di
|
||||
finally:
|
||||
clear_thread_tool_whitelist()
|
||||
# Attribute the fork's usage to the parent session; teardown never raises.
|
||||
for step in (
|
||||
lambda: _record_review_usage_to_parent(parent_agent, _snapshot_review_usage(fork)),
|
||||
fork.shutdown_memory_provider,
|
||||
fork.close,
|
||||
):
|
||||
for step in (lambda: _record_review_usage_to_parent(parent_agent, _snapshot_review_usage(fork)),
|
||||
fork.shutdown_memory_provider, fork.close):
|
||||
try:
|
||||
step()
|
||||
except Exception:
|
||||
@@ -160,13 +146,8 @@ def _answer_via_oneshot(question: str, history: Optional[List[Dict[str, Any]]],
|
||||
|
||||
|
||||
def answer_side_question(
|
||||
question: str,
|
||||
history: Optional[List[Dict[str, Any]]],
|
||||
*,
|
||||
parent_agent: Any = None,
|
||||
main_runtime: Optional[Dict[str, Any]] = None,
|
||||
max_tokens: int = 2048,
|
||||
temperature: Optional[float] = 0.3,
|
||||
question: str, history: Optional[List[Dict[str, Any]]], *, parent_agent: Any = None,
|
||||
main_runtime: Optional[Dict[str, Any]] = None, max_tokens: int = 2048, temperature: Optional[float] = 0.3,
|
||||
timeout: float = 180.0,
|
||||
) -> str:
|
||||
"""Fork when ``parent_agent`` is live, else (or on empty answer / failure) the one-shot
|
||||
|
||||
@@ -32,8 +32,7 @@ def is_thinking_timeout(classified: object, model: str, error_msg: str) -> bool:
|
||||
return False
|
||||
if get_reasoning_stale_timeout_floor(model) is None:
|
||||
return False
|
||||
error_msg_lower = (error_msg or "").lower()
|
||||
return any(p in error_msg_lower for p in _THINKING_TIMEOUT_SUBSTRINGS)
|
||||
return any(p in (error_msg or "").lower() for p in _THINKING_TIMEOUT_SUBSTRINGS)
|
||||
|
||||
|
||||
def build_thinking_timeout_guidance(provider: str, model: str, model_label: Optional[str] = None) -> str:
|
||||
|
||||
Reference in New Issue
Block a user