fix: use lazy logging with %s formatting in logger calls

Replace f-string interpolation in logger calls with lazy %-style
formatting across 10 files (38 instances). This follows Python logging
best practices — the message is only formatted if the log level is
enabled, avoiding unnecessary string concatenation overhead.

Files changed:
- trajectory_compressor.py (6)
- mini_swe_runner.py (2)
- agent/tool_executor.py (1)
- agent/model_metadata.py (1)
- agent/agent_runtime_helpers.py (3)
- agent/chat_completion_helpers.py (3)
- agent/conversation_loop.py (8)
- tools/skills_hub.py (2)
- tools/environments/docker.py (10)
- gateway/kanban_watchers.py (2)
This commit is contained in:
AlexFucuson9 2026-06-30 17:08:34 +07:00 committed by kshitij
parent 2bf2bc141d
commit 9eb8e20c68
9 changed files with 37 additions and 37 deletions

View File

@ -151,7 +151,7 @@ def convert_to_trajectory_format(agent, messages: List[Dict[str, Any]], user_que
except json.JSONDecodeError:
# This shouldn't happen since we validate and retry during conversation,
# but if it does, log warning and use empty dict
logger.warning(f"Unexpected invalid JSON in trajectory conversion: {tool_call['function']['arguments'][:100]}")
logger.warning("Unexpected invalid JSON in trajectory conversion: %s", tool_call['function']['arguments'][:100])
arguments = {}
tool_call_json = {
@ -1210,7 +1210,7 @@ def recover_with_credential_pool(
refreshed_id,
)
return False, has_retried_429
_ra().logger.info(f"Credential auth failure — refreshed pool entry {getattr(refreshed, 'id', '?')}")
_ra().logger.info("Credential auth failure — refreshed pool entry %s", getattr(refreshed, 'id', '?'))
agent._swap_credential(refreshed)
return True, has_retried_429
# Refresh failed — rotate to next credential instead of giving up.
@ -1835,7 +1835,7 @@ def dump_api_request_debug(
return dump_file
except Exception as dump_error:
if agent.verbose_logging:
logger.warning(f"Failed to dump API request debug payload: {dump_error}")
logger.warning("Failed to dump API request debug payload: %s", dump_error)
return None

View File

@ -2383,7 +2383,7 @@ def handle_max_iterations(agent, messages: list, api_call_count: int) -> str:
final_response = "I reached the iteration limit and couldn't generate a summary."
except Exception as e:
logger.warning(f"Failed to get summary response: {e}")
logger.warning("Failed to get summary response: %s", e)
final_response = f"I reached the maximum iterations ({agent.max_iterations}) but couldn't summarize. Error: {str(e)}"
finally:
from agent import relay_llm
@ -2424,7 +2424,7 @@ def cleanup_task_resources(agent, task_id: str) -> None:
_ra().cleanup_vm(task_id)
except Exception as e:
if agent.verbose_logging:
logger.warning(f"Failed to cleanup VM for task {task_id}: {e}")
logger.warning("Failed to cleanup VM for task %s: %s", task_id, e)
try:
headed = False
try:
@ -2442,7 +2442,7 @@ def cleanup_task_resources(agent, task_id: str) -> None:
_ra().cleanup_browser(task_id)
except Exception as e:
if agent.verbose_logging:
logger.warning(f"Failed to cleanup browser for task {task_id}: {e}")
logger.warning("Failed to cleanup browser for task %s: %s", task_id, e)
def _build_partial_stream_stub(

View File

@ -2672,7 +2672,7 @@ def run_conversation(
# Terminal — flush buffered retry trace so user sees what happened.
agent._flush_status_buffer()
agent._emit_status(f"❌ Max retries ({max_retries}) exceeded for invalid responses. Giving up.")
logger.error(f"{agent.log_prefix}Invalid API response after {max_retries} retries.")
logger.error("%sInvalid API response after %d retries.", agent.log_prefix, max_retries)
agent._persist_session(messages, conversation_history)
_final_response = f"Invalid API response after {max_retries} retries: {_failure_hint}"
return {
@ -2687,7 +2687,7 @@ def run_conversation(
# Backoff before retry — jittered exponential: 5s base, 120s cap
wait_time = jittered_backoff(retry_count, base_delay=5.0, max_delay=120.0)
agent._buffer_vprint(f"⏳ Retrying in {wait_time:.1f}s ({_failure_hint})...")
logger.warning(f"Invalid API response (retry {retry_count}/{max_retries}): {', '.join(error_details)} | Provider: {provider_name}")
logger.warning("Invalid API response (retry %d/%d): %s | Provider: %s", retry_count, max_retries, ', '.join(error_details), provider_name)
# Sleep in small increments to stay responsive to interrupts
sleep_end = time.time() + wait_time
@ -4599,7 +4599,7 @@ def run_conversation(
agent._flush_status_buffer()
agent._vprint(f"{agent.log_prefix}❌ Max compression attempts ({max_compression_attempts}) reached for payload-too-large error.", force=True)
agent._vprint(f"{agent.log_prefix} 💡 Try /new to start a fresh conversation, or /compress to retry compression.", force=True)
logger.error(f"{agent.log_prefix}413 compression failed after {max_compression_attempts} attempts.")
logger.error("%s413 compression failed after %d attempts.", agent.log_prefix, max_compression_attempts)
agent._persist_session(messages, conversation_history)
_final_response = f"Request payload too large: max compression attempts ({max_compression_attempts}) reached."
return {
@ -4668,7 +4668,7 @@ def run_conversation(
agent._flush_status_buffer()
agent._vprint(f"{agent.log_prefix}❌ Payload too large and cannot compress further.", force=True)
agent._vprint(f"{agent.log_prefix} 💡 Try /new to start a fresh conversation, or /compress to retry compression.", force=True)
logger.error(f"{agent.log_prefix}413 payload too large. Cannot compress further.")
logger.error("%s413 payload too large. Cannot compress further.", agent.log_prefix)
agent._persist_session(messages, conversation_history)
_final_response = "Request payload too large (413). Cannot compress further."
return {
@ -4741,7 +4741,7 @@ def run_conversation(
agent._flush_status_buffer()
agent._vprint(f"{agent.log_prefix}❌ Max compression attempts ({max_compression_attempts}) reached.", force=True)
agent._vprint(f"{agent.log_prefix} 💡 Try /new to start a fresh conversation, or /compress to retry compression.", force=True)
logger.error(f"{agent.log_prefix}Context compression failed after {max_compression_attempts} attempts.")
logger.error("%sContext compression failed after %d attempts.", agent.log_prefix, max_compression_attempts)
agent._persist_session(messages, conversation_history)
_final_response = f"Context length exceeded: max compression attempts ({max_compression_attempts}) reached."
return {
@ -4860,7 +4860,7 @@ def run_conversation(
agent._flush_status_buffer()
agent._vprint(f"{agent.log_prefix}❌ Max compression attempts ({max_compression_attempts}) reached.", force=True)
agent._vprint(f"{agent.log_prefix} 💡 Try /new to start a fresh conversation, or /compress to retry compression.", force=True)
logger.error(f"{agent.log_prefix}Context compression failed after {max_compression_attempts} attempts.")
logger.error("%sContext compression failed after %d attempts.", agent.log_prefix, max_compression_attempts)
agent._persist_session(messages, conversation_history)
_final_response = f"Context length exceeded: max compression attempts ({max_compression_attempts}) reached."
return {
@ -4918,7 +4918,7 @@ def run_conversation(
agent._flush_status_buffer()
agent._vprint(f"{agent.log_prefix}❌ Context length exceeded and cannot compress further.", force=True)
agent._vprint(f"{agent.log_prefix} 💡 The conversation has accumulated too much content. Try /new to start fresh, or /compress to manually trigger compression.", force=True)
logger.error(f"{agent.log_prefix}Context length exceeded: {new_tokens:,} tokens. Cannot compress further.")
logger.error("%sContext length exceeded: %s tokens. Cannot compress further.", agent.log_prefix, f"{new_tokens:,}")
agent._persist_session(messages, conversation_history)
_final_response = f"Context length exceeded ({new_tokens:,} tokens). Cannot compress further."
return {
@ -5184,7 +5184,7 @@ def run_conversation(
f"{agent.log_prefix} for localhost, or add the server's cert to your trust store.",
force=True,
)
logger.error(f"{agent.log_prefix}Non-retryable client error: {api_error}")
logger.error("%sNon-retryable client error: %s", agent.log_prefix, api_error)
# Skip session persistence when the error is likely
# context-overflow related (status 400 + large session).
# Persisting the failed user message would make the

View File

@ -1084,7 +1084,7 @@ def fetch_model_metadata(force_refresh: bool = False) -> Dict[str, Dict[str, Any
return cache
except Exception as e:
logger.warning(f"Failed to fetch model metadata from OpenRouter: {e}")
logger.warning("Failed to fetch model metadata from OpenRouter: %s", e)
if _model_metadata_cache:
return _model_metadata_cache
disk_cache = _load_model_metadata_disk_cache()

View File

@ -1038,7 +1038,7 @@ class GatewayKanbanWatchersMixin:
# Read max_spawn config to limit concurrent kanban tasks
max_spawn = kanban_cfg.get("max_spawn", None)
if max_spawn is not None:
logger.info(f"kanban dispatcher: max_spawn={max_spawn}")
logger.info("kanban dispatcher: max_spawn=%s", max_spawn)
# Cap the number of simultaneously running tasks so slow workers
# (local LLMs, resource-constrained hosts) don't pile up and time
@ -1063,7 +1063,7 @@ class GatewayKanbanWatchersMixin:
)
max_in_progress = None
else:
logger.info(f"kanban dispatcher: max_in_progress={max_in_progress}")
logger.info("kanban dispatcher: max_in_progress=%s", max_in_progress)
raw_failure_limit = kanban_cfg.get("failure_limit", _kb.DEFAULT_FAILURE_LIMIT)
try:

View File

@ -467,7 +467,7 @@ Complete the user's task step by step."""
response = self.client.chat.completions.create(**api_kwargs)
except Exception as e:
self.logger.error(f"API call failed: {e}")
self.logger.error("API call failed: %s", e)
break
assistant_message = response.choices[0].message
@ -607,7 +607,7 @@ Complete the user's task step by step."""
print(f"✅ Task {i} completed (api_calls={result['api_calls']})")
except Exception as e:
self.logger.error(f"Error on task {i}: {e}")
self.logger.error("Error on task %s: %s", i, e)
error_result = {
"conversations": [],
"completed": False,

View File

@ -886,10 +886,10 @@ class DockerEnvironment(BaseEnvironment):
self._container_name: str = ""
self._image_uses_s6_init: bool = False
self._all_run_args: list[str] = []
logger.info(f"DockerEnvironment volumes: {volumes}")
logger.info("DockerEnvironment volumes: %s", volumes)
# Ensure volumes is a list (config.yaml could be malformed)
if volumes is not None and not isinstance(volumes, list):
logger.warning(f"docker_volumes config is not a list: {volumes!r}")
logger.warning("docker_volumes config is not a list: %r", volumes)
volumes = []
# Fail fast if Docker is not available.
@ -933,7 +933,7 @@ class DockerEnvironment(BaseEnvironment):
workspace_explicitly_mounted = False
for vol in (volumes or []):
if not isinstance(vol, str):
logger.warning(f"Docker volume entry is not a string: {vol!r}")
logger.warning("Docker volume entry is not a string: %r", vol)
continue
vol = vol.strip()
if not vol:
@ -943,7 +943,7 @@ class DockerEnvironment(BaseEnvironment):
if ":/workspace" in vol:
workspace_explicitly_mounted = True
else:
logger.warning(f"Docker volume '{vol}' missing colon, skipping")
logger.warning("Docker volume '%s' missing colon, skipping", vol)
host_cwd_abs = os.path.abspath(os.path.expanduser(host_cwd)) if host_cwd else ""
bind_host_cwd = (
@ -953,7 +953,7 @@ class DockerEnvironment(BaseEnvironment):
and not workspace_explicitly_mounted
)
if auto_mount_cwd and host_cwd and not os.path.isdir(host_cwd_abs):
logger.debug(f"Skipping docker cwd mount: host_cwd is not a valid directory: {host_cwd}")
logger.debug("Skipping docker cwd mount: host_cwd is not a valid directory: %s", host_cwd)
self._workspace_dir: Optional[str] = None
self._home_dir: Optional[str] = None
@ -982,7 +982,7 @@ class DockerEnvironment(BaseEnvironment):
])
if bind_host_cwd:
logger.info(f"Mounting configured host cwd to /workspace: {host_cwd_abs}")
logger.info("Mounting configured host cwd to /workspace: %s", host_cwd_abs)
volume_args = ["-v", f"{host_cwd_abs}:/workspace", *volume_args]
elif workspace_explicitly_mounted:
logger.debug("Skipping docker cwd mount: /workspace already mounted by user config")
@ -1298,7 +1298,7 @@ class DockerEnvironment(BaseEnvironment):
run_exec=image_uses_s6_init,
)
logger.info(f"Docker volume_args: {volume_args}")
logger.info("Docker volume_args: %s", volume_args)
# User-supplied extra docker run flags (docker_extra_args in config.yaml).
# Appended last so they can override defaults if needed.
validated_extra = []
@ -1336,7 +1336,7 @@ class DockerEnvironment(BaseEnvironment):
+ env_args
+ validated_extra
)
logger.info(f"Docker run_args: {all_run_args}")
logger.info("Docker run_args: %s", all_run_args)
# Start the container directly via `docker run -d`.
container_name = f"hermes-{uuid.uuid4().hex[:8]}"
@ -1465,7 +1465,7 @@ class DockerEnvironment(BaseEnvironment):
image,
"sleep", "infinity", # no fixed lifetime — idle reaper handles cleanup
]
logger.debug(f"Starting container: {' '.join(run_cmd)}")
logger.debug("Starting container: %s", ' '.join(run_cmd))
try:
result = subprocess.run(
run_cmd,
@ -1494,7 +1494,7 @@ class DockerEnvironment(BaseEnvironment):
)
raise
self._container_id = result.stdout.strip()
logger.info(f"Started container {container_name} ({self._container_id[:12]})")
logger.info("Started container %s (%s)", container_name, self._container_id[:12])
# Build the init-time env forwarding args used to seed the snapshot.
self._init_env_args = self._build_init_env_args()

View File

@ -464,7 +464,7 @@ class GitHubAuth:
if resp.status_code == 201:
return resp.json().get("token")
except Exception as e:
logger.debug(f"GitHub App auth failed: {e}")
logger.debug("GitHub App auth failed: %s", e)
return None
@ -625,7 +625,7 @@ class GitHubSource(SkillSource):
if query_lower in searchable:
results.append(skill)
except Exception as e:
logger.debug(f"Failed to search {tap['repo']}: {e}")
logger.debug("Failed to search %s: %s", tap['repo'], e)
continue
# Deduplicate by identifier, preferring higher trust levels.

View File

@ -663,7 +663,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
except Exception as e:
metrics.summarization_errors += 1
self.logger.warning(f"Summarization attempt {attempt + 1} failed: {e}")
self.logger.warning("Summarization attempt %d failed: %s", attempt + 1, e)
if attempt < self.config.max_retries - 1:
time.sleep(jittered_backoff(attempt + 1, base_delay=self.config.retry_delay, max_delay=30.0))
@ -732,7 +732,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
except Exception as e:
metrics.summarization_errors += 1
self.logger.warning(f"Summarization attempt {attempt + 1} failed: {e}")
self.logger.warning("Summarization attempt %d failed: %s", attempt + 1, e)
if attempt < self.config.max_retries - 1:
await asyncio.sleep(jittered_backoff(attempt + 1, base_delay=self.config.retry_delay, max_delay=30.0))
@ -1087,7 +1087,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
jsonl_files = sorted(input_dir.glob("*.jsonl"))
if not jsonl_files:
self.logger.warning(f"No JSONL files found in {input_dir}")
self.logger.warning("No JSONL files found in %s", input_dir)
return
# Load ALL entries from all files
@ -1103,7 +1103,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
entry = json.loads(line)
all_entries.append((file_path, line_num, entry))
except json.JSONDecodeError as e:
self.logger.warning(f"Skipping invalid JSON at {file_path}:{line_num}: {e}")
self.logger.warning("Skipping invalid JSON at %s:%s: %s", file_path, line_num, e)
total_entries = len(all_entries)
@ -1172,7 +1172,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
)
except asyncio.TimeoutError:
self.logger.warning(f"Timeout processing entry from {file_path}:{entry_idx} (>{self.config.per_trajectory_timeout}s)")
self.logger.warning("Timeout processing entry from %s:%s (>%ss)", file_path, entry_idx, self.config.per_trajectory_timeout)
async with progress_lock:
self.aggregate_metrics.trajectories_failed += 1
@ -1188,7 +1188,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
results[file_path][entry_idx] = None
except Exception as e:
self.logger.error(f"Error processing entry from {file_path}:{entry_idx}: {e}")
self.logger.error("Error processing entry from %s:%s: %s", file_path, entry_idx, e)
async with progress_lock:
self.aggregate_metrics.trajectories_failed += 1