GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing flypython.com — flypython.com """A deterministic troubleshooting agent with bounded, testable tools."""
from __future__ import annotations
import math
import queue
import re
import threading
import time
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import TypeVar, cast
PLAYBOOKS = {
"network": "Check the timeout, retry transient failures with backoff, and log the final status code.",
"dependency": "Recreate the environment, pin the dependency, and record the failing version.",
"general": "Reproduce the issue with the smallest input, capture the error, and add a regression test.",
}
@dataclass(frozen=True)
class Action:
name: str
arguments: dict[str, str] = field(default_factory=dict)
@dataclass
class Task:
request: str
observations: list[str] = field(default_factory=list)
trace: list[str] = field(default_factory=list)
answer: str | None = None
def classify_issue(text: str) -> str:
if type(text) is not str:
raise TypeError("text must be a string")
lowered = text.casefold()
if any(
re.search(pattern, lowered)
for pattern in (
r"\btimeouts?\b",
r"\btime(?:d)?\s+out\b",
r"\bnetworks?\b",
r"\bapis?\b",
)
):
return "network"
if any(
re.search(pattern, lowered)
for pattern in (
r"\bimports?\b",
r"\bpackages?\b",
r"\bdependenc(?:y|ies)\b",
)
):
return "dependency"
return "general"
def lookup_playbook(category: str) -> str:
if type(category) is not str:
raise TypeError("category must be a string")
if category not in PLAYBOOKS:
raise ValueError(f"Unknown category: {category}")
return PLAYBOOKS[category]
TOOLS: dict[str, Callable[..., str]] = {
"classify_issue": classify_issue,
"lookup_playbook": lookup_playbook,
}
EXPECTED_ARGUMENTS = {
"classify_issue": {"text": str},
"lookup_playbook": {"category": str},
}
T = TypeVar("T")
class _CallTimedOut(Exception):
"""Internal signal used to distinguish call timeouts from user errors."""
def _validate_action(action: object) -> Action:
if not isinstance(action, Action):
raise TypeError("Planner must return an Action")
if type(action.name) is not str:
raise TypeError("Action name must be a string")
if type(action.arguments) is not dict:
raise TypeError("Action arguments must be a dictionary")
if any(type(key) is not str for key in action.arguments):
raise TypeError("Action argument names must be strings")
return action
def _validate_arguments(
action_name: str,
arguments: dict[str, object],
schema: dict[str, type],
) -> None:
if set(arguments) != set(schema):
raise ValueError(f"Invalid arguments for {action_name}")
for name, expected_type in schema.items():
if type(arguments[name]) is not expected_type:
raise TypeError(
f"Argument '{name}' for {action_name} must be "
f"{expected_type.__name__}"
)
def _validate_task(task: object) -> Task:
if not isinstance(task, Task):
raise TypeError("task must be a Task")
if type(task.request) is not str:
raise TypeError("Task request must be a string")
if type(task.observations) is not list or any(
type(item) is not str for item in task.observations
):
raise TypeError("Task observations must be a list of strings")
if type(task.trace) is not list or any(
type(item) is not str for item in task.trace
):
raise TypeError("Task trace must be a list of strings")
if task.answer is not None and type(task.answer) is not str:
raise TypeError("Task answer must be a string or None")
return task
def _validate_timeout(name: str, value: float | None) -> float | None:
if value is None:
return None
if type(value) not in (int, float) or not math.isfinite(value) or value <= 0:
raise ValueError(f"{name} must be a positive finite number or None")
return float(value)
def _call_with_timeout(
callback: Callable[[], T], timeout_seconds: float | None
) -> T:
"""Return on timeout even if a provider or tool call remains blocked.
Python threads cannot be forcibly stopped. A timed-out callback continues in
a daemon thread, so real providers and tools still need their own transport
timeouts to cancel network or subprocess work and prevent external effects.
"""
if timeout_seconds is None:
return callback()
results: queue.Queue[tuple[bool, object]] = queue.Queue(maxsize=1)
def invoke() -> None:
try:
results.put((True, callback()))
except BaseException as error:
results.put((False, error))
worker = threading.Thread(target=invoke, daemon=True)
worker.start()
worker.join(timeout_seconds)
if worker.is_alive():
raise _CallTimedOut
succeeded, result = results.get_nowait()
if not succeeded:
raise cast(BaseException, result)
return cast(T, result)
def _bounded_call(
callback: Callable[[], T],
*,
label: str,
call_timeout_seconds: float | None,
deadline_at: float | None,
deadline_seconds: float | None,
) -> T:
remaining = None if deadline_at is None else deadline_at - time.monotonic()
if remaining is not None and remaining <= 0:
raise TimeoutError(
f"Agent exceeded the {deadline_seconds:g}-second wall-clock deadline"
)
effective_timeout = call_timeout_seconds
deadline_is_limit = False
if remaining is not None and (
effective_timeout is None or remaining <= effective_timeout
):
effective_timeout = remaining
deadline_is_limit = True
try:
result = _call_with_timeout(callback, effective_timeout)
except _CallTimedOut:
if deadline_is_limit:
raise TimeoutError(
f"Agent exceeded the {deadline_seconds:g}-second wall-clock deadline"
) from None
raise TimeoutError(
f"{label} exceeded its {call_timeout_seconds:g}-second timeout"
) from None
if deadline_at is not None and time.monotonic() >= deadline_at:
raise TimeoutError(
f"Agent exceeded the {deadline_seconds:g}-second wall-clock deadline"
)
return result
def decide(task: Task) -> Action:
"""Stand in for a model so every action stays free and deterministic."""
if len(task.observations) == 0:
return Action("classify_issue", {"text": task.request})
if len(task.observations) == 1:
return Action("lookup_playbook", {"category": task.observations[0]})
return Action("finish", {"answer": task.observations[-1]})
def execute(action: Action) -> str:
action = _validate_action(action)
tool = TOOLS.get(action.name)
if tool is None:
raise ValueError(f"Unknown tool: {action.name}")
_validate_arguments(
action.name, action.arguments, EXPECTED_ARGUMENTS[action.name]
)
result = tool(**action.arguments)
if type(result) is not str:
raise TypeError(f"Tool {action.name} must return a string")
return result
def run_agent(
task: Task,
planner: Callable[[Task], Action] = decide,
max_steps: int = 3,
*,
deadline_seconds: float | None = 5.0,
planner_timeout_seconds: float | None = 2.0,
tool_timeout_seconds: float | None = 2.0,
) -> Task:
"""Run within separate step, wall-clock, planner, and tool budgets.
``max_steps`` limits decisions, not elapsed time. ``deadline_seconds`` caps
the whole run, while the planner and tool timeouts cap individual calls.
Passing ``None`` disables a time limit. Transport-level timeouts are still
required to cancel the underlying provider, network, or subprocess work.
"""
task = _validate_task(task)
if not callable(planner):
raise TypeError("planner must be callable")
if type(max_steps) is not int or max_steps < 1:
raise ValueError("max_steps must be at least 1")
deadline_seconds = _validate_timeout("deadline_seconds", deadline_seconds)
planner_timeout_seconds = _validate_timeout(
"planner_timeout_seconds", planner_timeout_seconds
)
tool_timeout_seconds = _validate_timeout(
"tool_timeout_seconds", tool_timeout_seconds
)
deadline_at = (
None if deadline_seconds is None else time.monotonic() + deadline_seconds
)
for step in range(1, max_steps + 1):
planner_task = Task(
request=task.request,
observations=list(task.observations),
trace=list(task.trace),
answer=task.answer,
)
action = _bounded_call(
lambda: planner(planner_task),
label="Planner",
call_timeout_seconds=planner_timeout_seconds,
deadline_at=deadline_at,
deadline_seconds=deadline_seconds,
)
action = _validate_action(action)
task.trace.append(f"{step}. {action.name}")
if action.name == "finish":
_validate_arguments("finish", action.arguments, {"answer": str})
task.answer = action.arguments["answer"]
return task
observation = _bounded_call(
lambda: execute(action),
label=f"Tool {action.name}",
call_timeout_seconds=tool_timeout_seconds,
deadline_at=deadline_at,
deadline_seconds=deadline_seconds,
)
task.observations.append(observation)
raise RuntimeError(f"Agent exceeded the {max_steps}-step budget")
if __name__ == "__main__":
result = run_agent(Task("My API request times out"))
print("\n".join(result.trace))
print(f"\nAnswer: {result.answer}")
|
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |