diff --git a/agent/agent_runtime_helpers.py b/agent/agent_runtime_helpers.py index 12bed0c666fd5..579314018cde4 100644 --- a/agent/agent_runtime_helpers.py +++ b/agent/agent_runtime_helpers.py @@ -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 diff --git a/agent/chat_completion_helpers.py b/agent/chat_completion_helpers.py index e40333f0e601b..e6e8ab7fdc1fc 100644 --- a/agent/chat_completion_helpers.py +++ b/agent/chat_completion_helpers.py @@ -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( diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py index 68453df309f77..1884c06489d9d 100644 --- a/agent/conversation_loop.py +++ b/agent/conversation_loop.py @@ -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 diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 6643ec820a6ed..f89d25f4c8dd7 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -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() diff --git a/gateway/kanban_watchers.py b/gateway/kanban_watchers.py index 5fbbdea07b1e3..dc2c5efd9011a 100644 --- a/gateway/kanban_watchers.py +++ b/gateway/kanban_watchers.py @@ -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: diff --git a/mini_swe_runner.py b/mini_swe_runner.py index 2853abc9a01e8..2994bfc6d25af 100644 --- a/mini_swe_runner.py +++ b/mini_swe_runner.py @@ -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, diff --git a/tools/environments/docker.py b/tools/environments/docker.py index 137d80627c3a5..780d571b71f83 100644 --- a/tools/environments/docker.py +++ b/tools/environments/docker.py @@ -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() diff --git a/tools/skills_hub.py b/tools/skills_hub.py index 56964c132561f..0316fee9d04b5 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -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. diff --git a/trajectory_compressor.py b/trajectory_compressor.py index 83248983a2cab..8feb4a5157cc4 100644 --- a/trajectory_compressor.py +++ b/trajectory_compressor.py @@ -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