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...
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 calledresult: the value returned by the tool- Returns
Trueif the tool call succeeded,Falseif 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:
resultisNone→ successresultis adictwithis_errortruthy → failureresultis astrstarting with"Error:"or"fatal:"→ failure- 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
- Session contracts — how execution counts affect enforcement
- Observability — audit events include
tool_successfor monitoring - Adapter comparison — how each adapter handles tool results
Last updated on