from __future__ import annotations
from forge.core.workflow import TextResponse, ToolCall
from forge.guardrails import Guardrails
guardrails = Guardrails(
tool_names=["search", "lookup", "answer"],
required_steps=["search", "lookup"],
terminal_tool="answer",
)
def handle_response_simple(response):
result = guardrails.check(response)
if result.action == "fatal":
return f"FATAL: {result.reason}"
if result.action in ("retry", "step_blocked"):
return f"{result.action}: {result.nudge.content[:80]}..."
tool_calls = result.tool_calls
executed = [tc.tool for tc in tool_calls] done = guardrails.record(executed)
return f"executed {executed}" + (" -- DONE" if done else "")
from forge.guardrails import ErrorTracker, ResponseValidator, StepEnforcer
validator = ResponseValidator(
tool_names=["search", "lookup", "answer"],
rescue_enabled=True,
)
enforcer = StepEnforcer(
required_steps=["search", "lookup"],
terminal_tool="answer",
)
errors = ErrorTracker(max_retries=3, max_tool_errors=2)
def handle_response_granular(response):
result = validator.validate(response)
if result.needs_retry:
errors.record_retry()
if errors.retries_exhausted:
return "FATAL: too many consecutive bad responses"
return f"retry: {result.nudge.content[:80]}..."
errors.reset_retries()
step_check = enforcer.check(result.tool_calls)
if step_check.needs_nudge:
return f"step_blocked: {step_check.nudge.content[:80]}..."
tool_calls = result.tool_calls
executed = [tc.tool for tc in tool_calls] for name in executed:
enforcer.record(name)
errors.reset_errors()
enforcer.reset_premature()
done = enforcer.terminal_reached(tool_calls)
return f"executed {executed}" + (" -- DONE" if done else "")
EXAMPLES_WORKFLOW = [
("Model returns plain text (no tool call)",
TextResponse(content="I think the answer is 42.")),
("Model jumps to terminal tool, skipping required steps",
[ToolCall(tool="answer", args={"text": "42"})]),
("Model calls first required step",
[ToolCall(tool="search", args={"query": "meaning of life"})]),
("Model calls second required step",
[ToolCall(tool="lookup", args={"id": "result-1"})]),
("Model calls terminal tool (steps now satisfied)",
[ToolCall(tool="answer", args={"text": "The answer is 42."})]),
]
from forge.tools import RESPOND_TOOL_NAME, respond_spec
respond_guardrails = Guardrails(
tool_names=["search", "lookup", "answer", RESPOND_TOOL_NAME],
required_steps=["search", "lookup"],
terminal_tool="answer",
)
def handle_response_with_respond(response):
result = respond_guardrails.check(response)
if result.action == "fatal":
return f"FATAL: {result.reason}"
if result.action in ("retry", "step_blocked"):
return f"{result.action}: {result.nudge.content[:80]}..."
tool_calls = result.tool_calls
for tc in tool_calls:
if tc.tool == RESPOND_TOOL_NAME:
message = tc.args.get("message", "")
return f"MODEL SAYS: {message}"
executed = [tc.tool for tc in tool_calls]
done = respond_guardrails.record(executed)
return f"executed {executed}" + (" -- DONE" if done else "")
EXAMPLES_RESPOND = [
("Model produces bare text (retried -- respond not used)",
TextResponse(content="Hello, how can I help?")),
("Model correctly uses respond for chat",
[ToolCall(tool="respond", args={"message": "Hello! How can I help you?"})]),
("Model calls a real tool",
[ToolCall(tool="search", args={"query": "meaning of life"})]),
("Model calls respond after completing work",
[ToolCall(tool="respond", args={"message": "I found the answer: 42."})]),
]
if __name__ == "__main__":
print("=== Part 1: Simple API (Guardrails) ===")
for i, (desc, response) in enumerate(EXAMPLES_WORKFLOW, 1):
print(f"\n{i}. {desc}")
print(f" {handle_response_simple(response)}")
enforcer._tracker.completed_steps.clear()
enforcer._premature_attempts = 0
errors._consecutive_retries = 0
print("\n\n=== Part 2: Granular API (individual components) ===")
for i, (desc, response) in enumerate(EXAMPLES_WORKFLOW, 1):
print(f"\n{i}. {desc}")
print(f" {handle_response_granular(response)}")
print("\n\n=== Part 3: Respond tool ===")
for i, (desc, response) in enumerate(EXAMPLES_RESPOND, 1):
print(f"\n{i}. {desc}")
print(f" {handle_response_with_respond(response)}")