christlurker's picture
FrontierAgent react demo
9d0fd45
Raw
History Blame Contribute Delete
6.67 kB
"""ToolMeta β€” metadata annotations for tools."""
from __future__ import annotations
from dataclasses import dataclass
from frontier_agent.infra.config import get_config
# Inline cap for exec/compute tool results before overflow-to-disk. Configurable
# via TOOL_EXEC_RESULT_MAX_CHARS (default 8K); shared by bash / run_python_code /
# the finance sandbox runners so the budget is tuned in one place.
_EXEC_RESULT_MAX_CHARS = get_config().tool_exec_result_max_chars
@dataclass(frozen=True)
class ToolMeta:
"""Metadata for a tool instance.
Attributes:
is_read_only: Tool only reads data, never modifies state.
is_destructive: Tool can cause irreversible changes (file deletion, etc.).
concurrency_safe: Safe to run in parallel with other tools.
timeout: Maximum execution time in seconds.
category: Logical grouping (web, file, compute, search, vision).
max_result_chars: Max characters to keep inline. Overflow saved to disk.
0 means no limit (default for most tools).
result_is_ranked: The result is ordered by RELEVANCE, best first, so a
contiguous head is worth more than a head-plus-tail split β€” the tail
of a ranked list is its worst entries. False (the default) means the
result is sequential: a transcript, a document, a file listing, an
exec log, where the end carries the verdict and is worth keeping.
Read by ``_overflow`` when TOOL_RESULT_TRUNCATION=auto.
"""
is_read_only: bool = True
is_destructive: bool = False
concurrency_safe: bool = True
timeout: int = 45
category: str = ""
max_result_chars: int = 0
result_is_ranked: bool = False
# ── Default metadata for built-in tools ──────────────────────────────────
TOOL_META: dict[str, ToolMeta] = {
# Web tools β€” read-only, concurrent-safe, longer timeout for network.
# max_result_chars=0 (no per-tool overflow): content density rewards
# full bodies (academic papers bury results 30K+ chars in), and the
# global TOOL_RESULT_MAX_CHARS + ReasoningStripCompactor backstop
# bound total context. Compaction ages old fetches to URL stubs so
# the model can re-fetch with info_to_extract if needed.
"web_search": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=30, category="web", result_is_ranked=True),
"web_fetch": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=60, category="web"),
"scholar_search": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=30, category="web", result_is_ranked=True),
# File read tools β€” read-only, concurrent-safe. max_result_chars=0
# for the same reason as the web tools: source files / long markdown
# / downloaded papers carry their signal across the whole body, and
# the global TOOL_RESULT_MAX_CHARS + the workflow compactor bound
# total context. file_editor_view also accepts a view_range arg the
# agent can use when it knows what slice it wants.
# read_file = structured/rich reader (sandbox-backed, may pip-install parsers
# + recalc spreadsheets), so a longer timeout than the plain-text read_text.
"read_file": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=120, category="file"),
# Reads this run's own transcript in-process β€” no sandbox, no network, so the
# timeout is a scan of an ~1 MB file. ``max_result_chars=0``: the tool caps and
# paginates its own slice, and a per-tool overflow on top would truncate the
# very content the call exists to recover.
"recover_result": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=30, category="file"),
"read_text": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=15, category="file"),
"file_editor_view": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=15, category="file"),
# File write tools β€” NOT read-only, NOT concurrent-safe (may conflict)
"create_file": ToolMeta(
is_read_only=False,
concurrency_safe=False,
timeout=120,
category="file",
),
"write_file": ToolMeta(is_read_only=False, concurrency_safe=False, timeout=15, category="file"),
"file_editor_create": ToolMeta(is_read_only=False, concurrency_safe=False, timeout=15, category="file"),
"file_editor_str_replace": ToolMeta(is_read_only=False, concurrency_safe=False, timeout=15, category="file"),
# Bash β€” NOT read-only, NOT concurrent-safe, potentially destructive
"bash": ToolMeta(is_read_only=False, is_destructive=True, concurrency_safe=False, timeout=60, category="compute", max_result_chars=_EXEC_RESULT_MAX_CHARS),
# Vision β€” read-only, concurrent-safe
"view_image": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=30, category="vision"),
# Search tools β€” read-only, concurrent-safe
"grep_search": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=30, category="search", max_result_chars=8_000),
"glob_search": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=30, category="search", max_result_chars=8_000),
# Delegation β€” spawns sub-agents, long-running, NOT concurrent-safe (shared evidence pool)
"delegate_subtask": ToolMeta(is_read_only=False, concurrency_safe=False, timeout=1900, category="orchestration"),
# Python sandbox β€” isolated code execution, concurrent-safe (each call is independent)
"run_python_code": ToolMeta(
is_read_only=False,
is_destructive=False,
concurrency_safe=True,
timeout=120,
category="compute",
max_result_chars=_EXEC_RESULT_MAX_CHARS,
),
# Meta β€” tool discovery
"tool_search": ToolMeta(is_read_only=True, concurrency_safe=True, timeout=5, category="meta"),
# Finance sandbox
"create_finance_sandbox": ToolMeta(
is_read_only=False,
concurrency_safe=False,
timeout=120,
category="finance",
max_result_chars=4_000,
),
"run_command_in_finance_sandbox": ToolMeta(
is_read_only=False,
concurrency_safe=False,
timeout=600,
category="finance",
max_result_chars=_EXEC_RESULT_MAX_CHARS,
),
"run_python_code_in_finance_sandbox": ToolMeta(
is_read_only=False,
concurrency_safe=False,
timeout=600,
category="finance",
max_result_chars=_EXEC_RESULT_MAX_CHARS,
),
}
def get_tool_meta(tool_name: str) -> ToolMeta:
"""Get metadata for a tool. Returns safe defaults if not found."""
return TOOL_META.get(tool_name, ToolMeta())