import logging
from typing import Optional
class ToolExecutionError(Exception):
"""
Exception raised when a tool fails to execute as expected.
Args:
message (str): User-facing error message.
code (str, optional): Machine-readable error code.
user_message (Optional[str], optional): Message safe to show to end users.
original_exception (Optional[Exception], optional): The original exception, if any.
"""
def __init__(self, message: str, code: str = "TOOL_EXECUTION_ERROR", user_message: Optional[str] = None, original_exception: Optional[Exception] = None):
super().__init__(message)
self.message = message
self.code = code
self.user_message = user_message or "An error occurred while executing the tool."
self.original_exception = original_exception
# Set up a basic logger for the tools package
logger = logging.getLogger("tools")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter('[%(asctime)s] %(levelname)s %(name)s: %(message)s')
handler.setFormatter(formatter)
if not logger.hasHandlers():
logger.addHandler(handler)
import json
import re
from jinja2 import Environment, FileSystemLoader
# Set up Jinja2 environment for prompt templates
env = Environment(loader=FileSystemLoader("prompts"))
def load_prompt(template_name: str, **vars) -> str:
tpl = env.get_template(template_name)
return tpl.render(**vars)
def clean_json(text: str) -> dict | list:
"""Remove ASCII control bytes and return parsed JSON."""
safe = re.sub(r"[\x00-\x1F\x7F]", "", text)
return json.loads(safe)
def extract_fenced_json(raw: str) -> str:
"""Get the text between …, or fallback to raw."""
m = re.search(r"([\s\S]*?)", raw)
return m.group(1).strip() if m else raw
def safe_parse(raw: str) -> dict | list:
"""Strip control chars then json.loads, with fallback."""
fragment = extract_fenced_json(raw)
clean = re.sub(r"[\x00-\x1F\x7F]", "", fragment)
try:
return json.loads(clean)
except json.JSONDecodeError:
# try to grab first {...} or [...]
m = re.search(r"(\{[\s\S]*\}|\[[\s\S]*\])", clean)
if m:
return json.loads(m.group(1))
raise