Edictum
Guides

Custom Tool Success Detection

The default heuristic for detecting tool failures only catches two patterns: strings starting with `"Error:"` or `"fatal:"`, and dicts with `{"is_error": tru...

AI Assistance

Right page if: your tools return errors in non-standard formats (HTTP status codes, {"ok": false}, tracebacks) and session contracts are enforcing at the wrong time. Wrong page if: you need to write session contracts themselves -- see https://docs.edictum.ai/docs/contracts/session-contracts. For general observability, see https://docs.edictum.ai/docs/guides/observability. Gotcha: the default heuristic only catches strings starting with "Error:" or "fatal:" and dicts with {"is_error": true}. Everything else is classified as success. All 8 adapters use the same default heuristic.

The default heuristic for detecting tool failures only catches two patterns: strings starting with "Error:" or "fatal:", and dicts with {"is_error": true}. If your tools return errors differently, Edictum misclassifies failures as successes — and session contracts that depend on accurate counts enforce at the wrong time.

from edictum import Edictum

guard = Edictum.from_yaml(
    "contracts.yaml",
    success_check=lambda tool_name, result: not (
        isinstance(result, dict) and result.get("status", 200) >= 400
    ),
)

How it works

The success_check parameter accepts a callable with the signature:

def success_check(tool_name: str, result: Any) -> bool:
    ...
  • tool_name: the name of the tool that was called
  • result: the value returned by the tool
  • Returns True if the tool call succeeded, False if it failed

When provided, it replaces the default heuristic in all 8 framework adapters and in Edictum.run(). When not provided, Edictum.run() uses the same default heuristic as the adapters (_default_success_check()), which checks for strings starting with "Error:" or "fatal:" and dicts with {"is_error": true}. All 8 adapters use the identical default heuristic.

The callable is passed through all factory methods:

# Constructor
guard = Edictum(success_check=my_checker, ...)

# YAML
guard = Edictum.from_yaml("contracts.yaml", success_check=my_checker)

# YAML string
guard = Edictum.from_yaml_string(yaml_content, success_check=my_checker)

# Template
guard = Edictum.from_template("file-agent", success_check=my_checker)

# Merged guards — uses the first guard's success_check
merged = Edictum.from_multiple([guard1, guard2])

Default heuristic

When no success_check is provided, the default heuristic checks:

  1. result is None → success
  2. result is a dict with is_error truthy → failure
  3. result is a str starting with "Error:" or "fatal:" → failure
  4. Everything else → success

Some adapters extend this with framework-specific checks (e.g., Semantic Kernel checks FunctionResult.metadata.error). A custom success_check replaces all of this — including the framework-specific checks.

Next steps

Last updated on

On this page