refactor: consolidate debug logging across tools with shared DebugSession class

- Introduced a new DebugSession class in tools/debug_helpers.py to centralize debug logging functionality, replacing duplicated code across various tool modules.
- Updated image_generation_tool.py, mixture_of_agents_tool.py, vision_tools.py, web_tools.py, and others to utilize the new DebugSession for logging tool calls and saving debug logs.
- Enhanced maintainability and consistency in debug logging practices across the codebase.
This commit is contained in:
teknium1
2026-02-21 03:53:24 -08:00
parent 748fd3db88
commit 7ee7221af1
7 changed files with 193 additions and 441 deletions

104
tools/debug_helpers.py Normal file
View File

@@ -0,0 +1,104 @@
"""Shared debug session infrastructure for Hermes tools.
Replaces the identical DEBUG_MODE / _log_debug_call / _save_debug_log /
get_debug_session_info boilerplate previously duplicated across web_tools,
vision_tools, mixture_of_agents_tool, and image_generation_tool.
Usage in a tool module:
from tools.debug_helpers import DebugSession
_debug = DebugSession("web_tools", env_var="WEB_TOOLS_DEBUG")
# Log a call (no-op when debug mode is off)
_debug.log_call("web_search", {"query": q, "results": len(r)})
# Save the debug log (no-op when debug mode is off)
_debug.save()
# Expose debug info to external callers
def get_debug_session_info():
return _debug.get_session_info()
"""
import datetime
import json
import logging
import os
import uuid
from pathlib import Path
from typing import Any, Dict
logger = logging.getLogger(__name__)
class DebugSession:
"""Per-tool debug session that records tool calls to a JSON log file.
Activated by a tool-specific environment variable (e.g. WEB_TOOLS_DEBUG=true).
When disabled, all methods are cheap no-ops.
"""
def __init__(self, tool_name: str, *, env_var: str) -> None:
self.tool_name = tool_name
self.enabled = os.getenv(env_var, "false").lower() == "true"
self.session_id = str(uuid.uuid4()) if self.enabled else ""
self.log_dir = Path("./logs")
self._calls: list[Dict[str, Any]] = []
self._start_time = datetime.datetime.now().isoformat() if self.enabled else ""
if self.enabled:
self.log_dir.mkdir(exist_ok=True)
logger.debug("%s debug mode enabled - Session ID: %s",
tool_name, self.session_id)
@property
def active(self) -> bool:
return self.enabled
def log_call(self, call_name: str, call_data: Dict[str, Any]) -> None:
"""Append a tool-call entry to the in-memory log."""
if not self.enabled:
return
self._calls.append({
"timestamp": datetime.datetime.now().isoformat(),
"tool_name": call_name,
**call_data,
})
def save(self) -> None:
"""Flush the in-memory log to a JSON file in the logs directory."""
if not self.enabled:
return
try:
filename = f"{self.tool_name}_debug_{self.session_id}.json"
filepath = self.log_dir / filename
payload = {
"session_id": self.session_id,
"start_time": self._start_time,
"end_time": datetime.datetime.now().isoformat(),
"debug_enabled": True,
"total_calls": len(self._calls),
"tool_calls": self._calls,
}
with open(filepath, "w", encoding="utf-8") as f:
json.dump(payload, f, indent=2, ensure_ascii=False)
logger.debug("%s debug log saved: %s", self.tool_name, filepath)
except Exception as e:
logger.error("Error saving %s debug log: %s", self.tool_name, e)
def get_session_info(self) -> Dict[str, Any]:
"""Return a summary dict suitable for returning from get_debug_session_info()."""
if not self.enabled:
return {
"enabled": False,
"session_id": None,
"log_path": None,
"total_calls": 0,
}
return {
"enabled": True,
"session_id": self.session_id,
"log_path": str(self.log_dir / f"{self.tool_name}_debug_{self.session_id}.json"),
"total_calls": len(self._calls),
}

View File

@@ -32,11 +32,10 @@ import json
import logging
import os
import asyncio
import uuid
import datetime
from pathlib import Path
from typing import Dict, Any, Optional, Union
import fal_client
from tools.debug_helpers import DebugSession
logger = logging.getLogger(__name__)
@@ -78,65 +77,7 @@ VALID_IMAGE_SIZES = [
VALID_OUTPUT_FORMATS = ["jpeg", "png"]
VALID_ACCELERATION_MODES = ["none", "regular", "high"]
# Debug mode configuration
DEBUG_MODE = os.getenv("IMAGE_TOOLS_DEBUG", "false").lower() == "true"
DEBUG_SESSION_ID = str(uuid.uuid4())
DEBUG_LOG_PATH = Path("./logs")
DEBUG_DATA = {
"session_id": DEBUG_SESSION_ID,
"start_time": datetime.datetime.now().isoformat(),
"debug_enabled": DEBUG_MODE,
"tool_calls": []
} if DEBUG_MODE else None
# Create logs directory if debug mode is enabled
if DEBUG_MODE:
DEBUG_LOG_PATH.mkdir(exist_ok=True)
logger.debug("Image generation debug mode enabled - Session ID: %s", DEBUG_SESSION_ID)
def _log_debug_call(tool_name: str, call_data: Dict[str, Any]) -> None:
"""
Log a debug call entry to the global debug data structure.
Args:
tool_name (str): Name of the tool being called
call_data (Dict[str, Any]): Data about the call including parameters and results
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
call_entry = {
"timestamp": datetime.datetime.now().isoformat(),
"tool_name": tool_name,
**call_data
}
DEBUG_DATA["tool_calls"].append(call_entry)
def _save_debug_log() -> None:
"""
Save the current debug data to a JSON file in the logs directory.
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
try:
debug_filename = f"image_tools_debug_{DEBUG_SESSION_ID}.json"
debug_filepath = DEBUG_LOG_PATH / debug_filename
# Update end time
DEBUG_DATA["end_time"] = datetime.datetime.now().isoformat()
DEBUG_DATA["total_calls"] = len(DEBUG_DATA["tool_calls"])
with open(debug_filepath, 'w', encoding='utf-8') as f:
json.dump(DEBUG_DATA, f, indent=2, ensure_ascii=False)
logger.debug("Image generation debug log saved: %s", debug_filepath)
except Exception as e:
logger.error("Error saving image generation debug log: %s", e)
_debug = DebugSession("image_tools", env_var="IMAGE_TOOLS_DEBUG")
def _validate_parameters(
@@ -423,8 +364,8 @@ async def image_generate_tool(
debug_call_data["generation_time"] = generation_time
# Log debug information
_log_debug_call("image_generate_tool", debug_call_data)
_save_debug_log()
_debug.log_call("image_generate_tool", debug_call_data)
_debug.save()
return json.dumps(response_data, indent=2, ensure_ascii=False)
@@ -441,8 +382,8 @@ async def image_generate_tool(
debug_call_data["error"] = error_msg
debug_call_data["generation_time"] = generation_time
_log_debug_call("image_generate_tool", debug_call_data)
_save_debug_log()
_debug.log_call("image_generate_tool", debug_call_data)
_debug.save()
return json.dumps(response_data, indent=2, ensure_ascii=False)
@@ -484,20 +425,7 @@ def get_debug_session_info() -> Dict[str, Any]:
Returns:
Dict[str, Any]: Dictionary containing debug session information
"""
if not DEBUG_MODE or not DEBUG_DATA:
return {
"enabled": False,
"session_id": None,
"log_path": None,
"total_calls": 0
}
return {
"enabled": True,
"session_id": DEBUG_SESSION_ID,
"log_path": str(DEBUG_LOG_PATH / f"image_tools_debug_{DEBUG_SESSION_ID}.json"),
"total_calls": len(DEBUG_DATA["tool_calls"])
}
return _debug.get_session_info()
if __name__ == "__main__":
@@ -532,9 +460,9 @@ if __name__ == "__main__":
print(f"🔍 Auto-upscaling with: {UPSCALER_MODEL} ({UPSCALER_FACTOR}x)")
# Show debug mode status
if DEBUG_MODE:
print(f"🐛 Debug mode ENABLED - Session ID: {DEBUG_SESSION_ID}")
print(f" Debug logs will be saved to: ./logs/image_tools_debug_{DEBUG_SESSION_ID}.json")
if _debug.active:
print(f"🐛 Debug mode ENABLED - Session ID: {_debug.session_id}")
print(f" Debug logs will be saved to: ./logs/image_tools_debug_{_debug.session_id}.json")
else:
print("🐛 Debug mode disabled (set IMAGE_TOOLS_DEBUG=true to enable)")

View File

@@ -49,30 +49,13 @@ import json
import logging
import os
import asyncio
import uuid
import datetime
from pathlib import Path
from typing import Dict, Any, List, Optional
from openai import AsyncOpenAI
from hermes_constants import OPENROUTER_BASE_URL
from tools.openrouter_client import get_async_client as _get_openrouter_client, check_api_key as check_openrouter_api_key
from tools.debug_helpers import DebugSession
logger = logging.getLogger(__name__)
_openrouter_client = None
def _get_openrouter_client():
"""Get or create the OpenRouter client (lazy initialization)."""
global _openrouter_client
if _openrouter_client is None:
api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key:
raise ValueError("OPENROUTER_API_KEY environment variable not set")
_openrouter_client = AsyncOpenAI(
api_key=api_key,
base_url=OPENROUTER_BASE_URL
)
return _openrouter_client
# Configuration for MoA processing
# Reference models - these generate diverse initial responses in parallel (OpenRouter slugs)
REFERENCE_MODELS = [
@@ -97,65 +80,7 @@ AGGREGATOR_SYSTEM_PROMPT = """You have been provided with a set of responses fro
Responses from models:"""
# Debug mode configuration
DEBUG_MODE = os.getenv("MOA_TOOLS_DEBUG", "false").lower() == "true"
DEBUG_SESSION_ID = str(uuid.uuid4())
DEBUG_LOG_PATH = Path("./logs")
DEBUG_DATA = {
"session_id": DEBUG_SESSION_ID,
"start_time": datetime.datetime.now().isoformat(),
"debug_enabled": DEBUG_MODE,
"tool_calls": []
} if DEBUG_MODE else None
# Create logs directory if debug mode is enabled
if DEBUG_MODE:
DEBUG_LOG_PATH.mkdir(exist_ok=True)
logger.debug("MoA debug mode enabled - Session ID: %s", DEBUG_SESSION_ID)
def _log_debug_call(tool_name: str, call_data: Dict[str, Any]) -> None:
"""
Log a debug call entry to the global debug data structure.
Args:
tool_name (str): Name of the tool being called
call_data (Dict[str, Any]): Data about the call including parameters and results
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
call_entry = {
"timestamp": datetime.datetime.now().isoformat(),
"tool_name": tool_name,
**call_data
}
DEBUG_DATA["tool_calls"].append(call_entry)
def _save_debug_log() -> None:
"""
Save the current debug data to a JSON file in the logs directory.
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
try:
debug_filename = f"moa_tools_debug_{DEBUG_SESSION_ID}.json"
debug_filepath = DEBUG_LOG_PATH / debug_filename
# Update end time
DEBUG_DATA["end_time"] = datetime.datetime.now().isoformat()
DEBUG_DATA["total_calls"] = len(DEBUG_DATA["tool_calls"])
with open(debug_filepath, 'w', encoding='utf-8') as f:
json.dump(DEBUG_DATA, f, indent=2, ensure_ascii=False)
logger.debug("MoA debug log saved: %s", debug_filepath)
except Exception as e:
logger.error("Error saving MoA debug log: %s", e)
_debug = DebugSession("moa_tools", env_var="MOA_TOOLS_DEBUG")
def _construct_aggregator_prompt(system_prompt: str, responses: List[str]) -> str:
@@ -432,8 +357,8 @@ async def mixture_of_agents_tool(
debug_call_data["models_used"] = result["models_used"]
# Log debug information
_log_debug_call("mixture_of_agents_tool", debug_call_data)
_save_debug_log()
_debug.log_call("mixture_of_agents_tool", debug_call_data)
_debug.save()
return json.dumps(result, indent=2, ensure_ascii=False)
@@ -458,22 +383,12 @@ async def mixture_of_agents_tool(
debug_call_data["error"] = error_msg
debug_call_data["processing_time_seconds"] = processing_time
_log_debug_call("mixture_of_agents_tool", debug_call_data)
_save_debug_log()
_debug.log_call("mixture_of_agents_tool", debug_call_data)
_debug.save()
return json.dumps(result, indent=2, ensure_ascii=False)
def check_openrouter_api_key() -> bool:
"""
Check if the OpenRouter API key is available in environment variables.
Returns:
bool: True if API key is set, False otherwise
"""
return bool(os.getenv("OPENROUTER_API_KEY"))
def check_moa_requirements() -> bool:
"""
Check if all requirements for MoA tools are met.
@@ -491,20 +406,7 @@ def get_debug_session_info() -> Dict[str, Any]:
Returns:
Dict[str, Any]: Dictionary containing debug session information
"""
if not DEBUG_MODE or not DEBUG_DATA:
return {
"enabled": False,
"session_id": None,
"log_path": None,
"total_calls": 0
}
return {
"enabled": True,
"session_id": DEBUG_SESSION_ID,
"log_path": str(DEBUG_LOG_PATH / f"moa_tools_debug_{DEBUG_SESSION_ID}.json"),
"total_calls": len(DEBUG_DATA["tool_calls"])
}
return _debug.get_session_info()
def get_available_models() -> Dict[str, List[str]]:
@@ -570,9 +472,9 @@ if __name__ == "__main__":
print(f" 📊 Minimum successful models: {config['min_successful_references']}")
# Show debug mode status
if DEBUG_MODE:
print(f"\n🐛 Debug mode ENABLED - Session ID: {DEBUG_SESSION_ID}")
print(f" Debug logs will be saved to: ./logs/moa_tools_debug_{DEBUG_SESSION_ID}.json")
if _debug.active:
print(f"\n🐛 Debug mode ENABLED - Session ID: {_debug.session_id}")
print(f" Debug logs will be saved to: ./logs/moa_tools_debug_{_debug.session_id}.json")
else:
print("\n🐛 Debug mode disabled (set MOA_TOOLS_DEBUG=true to enable)")

View File

@@ -0,0 +1,34 @@
"""Shared OpenRouter API client for Hermes tools.
Provides a single lazy-initialized AsyncOpenAI client that all tool modules
can share, eliminating the duplicated _get_openrouter_client() /
_get_summarizer_client() pattern previously copy-pasted across web_tools,
vision_tools, mixture_of_agents_tool, and session_search_tool.
"""
import os
from openai import AsyncOpenAI
from hermes_constants import OPENROUTER_BASE_URL
_client: AsyncOpenAI | None = None
def get_async_client() -> AsyncOpenAI:
"""Return a shared AsyncOpenAI client pointed at OpenRouter.
The client is created lazily on first call and reused thereafter.
Raises ValueError if OPENROUTER_API_KEY is not set.
"""
global _client
if _client is None:
api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key:
raise ValueError("OPENROUTER_API_KEY environment variable not set")
_client = AsyncOpenAI(api_key=api_key, base_url=OPENROUTER_BASE_URL)
return _client
def check_api_key() -> bool:
"""Check whether the OpenRouter API key is present."""
return bool(os.getenv("OPENROUTER_API_KEY"))

View File

@@ -22,29 +22,12 @@ import os
import logging
from typing import Dict, Any, List, Optional
from openai import AsyncOpenAI
from hermes_constants import OPENROUTER_BASE_URL
from tools.openrouter_client import get_async_client as _get_client
SUMMARIZER_MODEL = "google/gemini-3-flash-preview"
MAX_SESSION_CHARS = 100_000
MAX_SUMMARY_TOKENS = 2000
_summarizer_client = None
def _get_client() -> AsyncOpenAI:
"""Lazy-init the summarizer client (shared with web_tools pattern)."""
global _summarizer_client
if _summarizer_client is None:
api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key:
raise ValueError("OPENROUTER_API_KEY not set")
_summarizer_client = AsyncOpenAI(
api_key=api_key,
base_url=OPENROUTER_BASE_URL,
)
return _summarizer_client
def _format_conversation(messages: List[Dict[str, Any]]) -> str:
"""Format session messages into a readable transcript for summarization."""

View File

@@ -32,93 +32,19 @@ import logging
import os
import asyncio
import uuid
import datetime
import base64
from pathlib import Path
from typing import Dict, Any, Optional
from openai import AsyncOpenAI
import httpx
from hermes_constants import OPENROUTER_BASE_URL
from tools.openrouter_client import get_async_client as _get_openrouter_client, check_api_key as check_openrouter_api_key
from tools.debug_helpers import DebugSession
logger = logging.getLogger(__name__)
_openrouter_client = None
def _get_openrouter_client():
"""Get or create the OpenRouter client (lazy initialization)."""
global _openrouter_client
if _openrouter_client is None:
api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key:
raise ValueError("OPENROUTER_API_KEY environment variable not set")
_openrouter_client = AsyncOpenAI(
api_key=api_key,
base_url=OPENROUTER_BASE_URL
)
return _openrouter_client
# Configuration for vision processing
DEFAULT_VISION_MODEL = "google/gemini-3-flash-preview"
# Debug mode configuration
DEBUG_MODE = os.getenv("VISION_TOOLS_DEBUG", "false").lower() == "true"
DEBUG_SESSION_ID = str(uuid.uuid4())
DEBUG_LOG_PATH = Path("./logs")
DEBUG_DATA = {
"session_id": DEBUG_SESSION_ID,
"start_time": datetime.datetime.now().isoformat(),
"debug_enabled": DEBUG_MODE,
"tool_calls": []
} if DEBUG_MODE else None
# Create logs directory if debug mode is enabled
if DEBUG_MODE:
DEBUG_LOG_PATH.mkdir(exist_ok=True)
logger.debug("Vision debug mode enabled - Session ID: %s", DEBUG_SESSION_ID)
def _log_debug_call(tool_name: str, call_data: Dict[str, Any]) -> None:
"""
Log a debug call entry to the global debug data structure.
Args:
tool_name (str): Name of the tool being called
call_data (Dict[str, Any]): Data about the call including parameters and results
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
call_entry = {
"timestamp": datetime.datetime.now().isoformat(),
"tool_name": tool_name,
**call_data
}
DEBUG_DATA["tool_calls"].append(call_entry)
def _save_debug_log() -> None:
"""
Save the current debug data to a JSON file in the logs directory.
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
try:
debug_filename = f"vision_tools_debug_{DEBUG_SESSION_ID}.json"
debug_filepath = DEBUG_LOG_PATH / debug_filename
# Update end time
DEBUG_DATA["end_time"] = datetime.datetime.now().isoformat()
DEBUG_DATA["total_calls"] = len(DEBUG_DATA["tool_calls"])
with open(debug_filepath, 'w', encoding='utf-8') as f:
json.dump(DEBUG_DATA, f, indent=2, ensure_ascii=False)
logger.debug("Vision debug log saved: %s", debug_filepath)
except Exception as e:
logger.error("Error saving vision debug log: %s", e)
_debug = DebugSession("vision_tools", env_var="VISION_TOOLS_DEBUG")
def _validate_image_url(url: str) -> bool:
@@ -395,8 +321,8 @@ async def vision_analyze_tool(
debug_call_data["analysis_length"] = analysis_length
# Log debug information
_log_debug_call("vision_analyze_tool", debug_call_data)
_save_debug_log()
_debug.log_call("vision_analyze_tool", debug_call_data)
_debug.save()
return json.dumps(result, indent=2, ensure_ascii=False)
@@ -411,8 +337,8 @@ async def vision_analyze_tool(
}
debug_call_data["error"] = error_msg
_log_debug_call("vision_analyze_tool", debug_call_data)
_save_debug_log()
_debug.log_call("vision_analyze_tool", debug_call_data)
_debug.save()
return json.dumps(result, indent=2, ensure_ascii=False)
@@ -426,16 +352,6 @@ async def vision_analyze_tool(
logger.warning("Could not delete temporary file: %s", cleanup_error)
def check_openrouter_api_key() -> bool:
"""
Check if the OpenRouter API key is available in environment variables.
Returns:
bool: True if API key is set, False otherwise
"""
return bool(os.getenv("OPENROUTER_API_KEY"))
def check_vision_requirements() -> bool:
"""
Check if all requirements for vision tools are met.
@@ -453,20 +369,7 @@ def get_debug_session_info() -> Dict[str, Any]:
Returns:
Dict[str, Any]: Dictionary containing debug session information
"""
if not DEBUG_MODE or not DEBUG_DATA:
return {
"enabled": False,
"session_id": None,
"log_path": None,
"total_calls": 0
}
return {
"enabled": True,
"session_id": DEBUG_SESSION_ID,
"log_path": str(DEBUG_LOG_PATH / f"vision_tools_debug_{DEBUG_SESSION_ID}.json"),
"total_calls": len(DEBUG_DATA["tool_calls"])
}
return _debug.get_session_info()
if __name__ == "__main__":
@@ -491,9 +394,9 @@ if __name__ == "__main__":
print(f"🧠 Using model: {DEFAULT_VISION_MODEL}")
# Show debug mode status
if DEBUG_MODE:
print(f"🐛 Debug mode ENABLED - Session ID: {DEBUG_SESSION_ID}")
print(f" Debug logs will be saved to: ./logs/vision_tools_debug_{DEBUG_SESSION_ID}.json")
if _debug.active:
print(f"🐛 Debug mode ENABLED - Session ID: {_debug.session_id}")
print(f" Debug logs will be saved to: ./logs/vision_tools_debug_{_debug.session_id}.json")
else:
print("🐛 Debug mode disabled (set VISION_TOOLS_DEBUG=true to enable)")

View File

@@ -45,18 +45,13 @@ import logging
import os
import re
import asyncio
import uuid
import datetime
from pathlib import Path
from typing import List, Dict, Any, Optional
from firecrawl import Firecrawl
from openai import AsyncOpenAI
from hermes_constants import OPENROUTER_BASE_URL
from tools.openrouter_client import get_async_client as _get_openrouter_client
from tools.debug_helpers import DebugSession
logger = logging.getLogger(__name__)
# Initialize Firecrawl client lazily (only when needed)
# This prevents import errors when FIRECRAWL_API_KEY is not set
_firecrawl_client = None
def _get_firecrawl_client():
@@ -69,85 +64,10 @@ def _get_firecrawl_client():
_firecrawl_client = Firecrawl(api_key=api_key)
return _firecrawl_client
# Initialize OpenRouter API client lazily (only when needed)
_summarizer_client = None
def _get_summarizer_client():
"""Get or create the summarizer client (lazy initialization)."""
global _summarizer_client
if _summarizer_client is None:
api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key:
raise ValueError("OPENROUTER_API_KEY environment variable not set")
_summarizer_client = AsyncOpenAI(
api_key=api_key,
base_url=OPENROUTER_BASE_URL
)
return _summarizer_client
# Configuration for LLM processing
DEFAULT_SUMMARIZER_MODEL = "google/gemini-3-flash-preview"
DEFAULT_MIN_LENGTH_FOR_SUMMARIZATION = 5000
# Debug mode configuration
DEBUG_MODE = os.getenv("WEB_TOOLS_DEBUG", "false").lower() == "true"
DEBUG_SESSION_ID = str(uuid.uuid4())
DEBUG_LOG_PATH = Path("./logs")
DEBUG_DATA = {
"session_id": DEBUG_SESSION_ID,
"start_time": datetime.datetime.now().isoformat(),
"debug_enabled": DEBUG_MODE,
"tool_calls": []
} if DEBUG_MODE else None
# Create logs directory if debug mode is enabled
if DEBUG_MODE:
DEBUG_LOG_PATH.mkdir(exist_ok=True)
logger.info("Debug mode enabled - Session ID: %s", DEBUG_SESSION_ID)
def _log_debug_call(tool_name: str, call_data: Dict[str, Any]) -> None:
"""
Log a debug call entry to the global debug data structure.
Args:
tool_name (str): Name of the tool being called
call_data (Dict[str, Any]): Data about the call including parameters and results
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
call_entry = {
"timestamp": datetime.datetime.now().isoformat(),
"tool_name": tool_name,
**call_data
}
DEBUG_DATA["tool_calls"].append(call_entry)
def _save_debug_log() -> None:
"""
Save the current debug data to a JSON file in the logs directory.
"""
if not DEBUG_MODE or not DEBUG_DATA:
return
try:
debug_filename = f"web_tools_debug_{DEBUG_SESSION_ID}.json"
debug_filepath = DEBUG_LOG_PATH / debug_filename
# Update end time
DEBUG_DATA["end_time"] = datetime.datetime.now().isoformat()
DEBUG_DATA["total_calls"] = len(DEBUG_DATA["tool_calls"])
with open(debug_filepath, 'w', encoding='utf-8') as f:
json.dump(DEBUG_DATA, f, indent=2, ensure_ascii=False)
logger.debug("Debug log saved: %s", debug_filepath)
except Exception as e:
logger.error("Error saving debug log: %s", e)
_debug = DebugSession("web_tools", env_var="WEB_TOOLS_DEBUG")
async def process_content_with_llm(
@@ -303,7 +223,7 @@ Create a markdown summary that captures all key information in a well-organized,
for attempt in range(max_retries):
try:
response = await _get_summarizer_client().chat.completions.create(
response = await _get_openrouter_client().chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system_prompt},
@@ -422,7 +342,7 @@ Synthesize these into ONE cohesive, comprehensive summary that:
Create a single, unified markdown summary."""
try:
response = await _get_summarizer_client().chat.completions.create(
response = await _get_openrouter_client().chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "You synthesize multiple summaries into one cohesive, comprehensive summary. Be thorough but concise."},
@@ -597,8 +517,8 @@ def web_search_tool(query: str, limit: int = 5) -> str:
debug_call_data["final_response_size"] = len(result_json)
# Log debug information
_log_debug_call("web_search_tool", debug_call_data)
_save_debug_log()
_debug.log_call("web_search_tool", debug_call_data)
_debug.save()
return result_json
@@ -607,8 +527,8 @@ def web_search_tool(query: str, limit: int = 5) -> str:
logger.error("%s", error_msg)
debug_call_data["error"] = error_msg
_log_debug_call("web_search_tool", debug_call_data)
_save_debug_log()
_debug.log_call("web_search_tool", debug_call_data)
_debug.save()
return json.dumps({"error": error_msg}, ensure_ascii=False)
@@ -859,8 +779,8 @@ async def web_extract_tool(
debug_call_data["processing_applied"].append("base64_image_removal")
# Log debug information
_log_debug_call("web_extract_tool", debug_call_data)
_save_debug_log()
_debug.log_call("web_extract_tool", debug_call_data)
_debug.save()
return cleaned_result
@@ -869,8 +789,8 @@ async def web_extract_tool(
logger.error("%s", error_msg)
debug_call_data["error"] = error_msg
_log_debug_call("web_extract_tool", debug_call_data)
_save_debug_log()
_debug.log_call("web_extract_tool", debug_call_data)
_debug.save()
return json.dumps({"error": error_msg}, ensure_ascii=False)
@@ -1149,8 +1069,8 @@ async def web_crawl_tool(
debug_call_data["processing_applied"].append("base64_image_removal")
# Log debug information
_log_debug_call("web_crawl_tool", debug_call_data)
_save_debug_log()
_debug.log_call("web_crawl_tool", debug_call_data)
_debug.save()
return cleaned_result
@@ -1159,8 +1079,8 @@ async def web_crawl_tool(
logger.error("%s", error_msg)
debug_call_data["error"] = error_msg
_log_debug_call("web_crawl_tool", debug_call_data)
_save_debug_log()
_debug.log_call("web_crawl_tool", debug_call_data)
_debug.save()
return json.dumps({"error": error_msg}, ensure_ascii=False)
@@ -1187,30 +1107,8 @@ def check_nous_api_key() -> bool:
def get_debug_session_info() -> Dict[str, Any]:
"""
Get information about the current debug session.
Returns:
Dict[str, Any]: Dictionary containing debug session information:
- enabled: Whether debug mode is enabled
- session_id: Current session UUID (if enabled)
- log_path: Path where debug logs are saved (if enabled)
- total_calls: Number of tool calls logged so far (if enabled)
"""
if not DEBUG_MODE or not DEBUG_DATA:
return {
"enabled": False,
"session_id": None,
"log_path": None,
"total_calls": 0
}
return {
"enabled": True,
"session_id": DEBUG_SESSION_ID,
"log_path": str(DEBUG_LOG_PATH / f"web_tools_debug_{DEBUG_SESSION_ID}.json"),
"total_calls": len(DEBUG_DATA["tool_calls"])
}
"""Get information about the current debug session."""
return _debug.get_session_info()
if __name__ == "__main__":
@@ -1249,9 +1147,9 @@ if __name__ == "__main__":
print(f" Default min length for processing: {DEFAULT_MIN_LENGTH_FOR_SUMMARIZATION} chars")
# Show debug mode status
if DEBUG_MODE:
print(f"🐛 Debug mode ENABLED - Session ID: {DEBUG_SESSION_ID}")
print(f" Debug logs will be saved to: ./logs/web_tools_debug_{DEBUG_SESSION_ID}.json")
if _debug.active:
print(f"🐛 Debug mode ENABLED - Session ID: {_debug.session_id}")
print(f" Debug logs will be saved to: {_debug.log_dir}/web_tools_debug_{_debug.session_id}.json")
else:
print("🐛 Debug mode disabled (set WEB_TOOLS_DEBUG=true to enable)")