You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: web search fallback chain + loop-guard rework (#467#544#571)
- _search_with_fallbacks now tries the configured fallback chain when the
primary backend reports failure OR when it returns empty results and a
fallback chain is configured. Empty results with no fallback chain remain
success=True so real 'no hits' outcomes are not turned into provider-dead
errors (#467 rework).
- DDGS provider surfaces provider-dead only when empty results occur and a
fallback chain exists.
- Update fallback-chain tests to match the new semantics and keep
regression coverage.
- Carry forward prior loop-guard / tool-diagnostics improvements from #544.
Closes#467Closes#544Closes#571
Co-Authored-By: Hermes Evolution <evolution@hermes.ai>
"The operation timed out / the resource is unreachable. Set it aside, route to a "
33
-
"fallback if one exists, and do NOT loop on health checks or retry blindly."),
34
-
(re.compile(r"\b(char(acter)?s?|byte)s?\b.*\b(limit|exceed|too (long|large|big)|maximum)\b|exceeds the maximum|max(imum)? (length|size)|too many tokens|context length", re.I),
35
-
"limit",
36
-
"A size/length limit was hit. Don't resend as-is — chunk the work, summarize, "
37
-
"or raise the relevant config limit; a near-identical retry will fail the same way."),
38
-
(re.compile(r"no such file or directory|not found|does not exist|cannot find|no matches found|0 results|no results", re.I),
39
-
"not_found",
40
-
"The target wasn't found. Re-check the path/name (it may be dynamic), broaden the "
41
-
"search, or create the prerequisite first — don't repeat the same lookup."),
42
-
(re.compile(r"traceback \(most recent call|exit code[:\s]+[1-9]|exit status [1-9]|non-zero exit|error:|exception|failed", re.I),
43
-
"runtime_error",
44
-
"The call errored. Read the message, fix the root cause, and CHANGE the call — "
45
-
"retrying the same arguments will reproduce it."),
22
+
(
23
+
re.compile(
24
+
r"command not found|not recognized as|: No such file or directory.*\b(sh|bash|exec)\b|executable file not found",
25
+
re.I,
26
+
),
27
+
"missing_command",
28
+
"A required binary/command is missing. Check prerequisites first "
29
+
"(`which <cmd>` / install it), or use a different tool — do NOT repeat the same command.",
30
+
),
31
+
(
32
+
re.compile(
33
+
r"permission denied|access denied|not permitted|forbidden|refusing to write|operation not permitted|EACCES",
34
+
re.I,
35
+
),
36
+
"permission",
37
+
"Access is denied and the agent can't elevate. Do NOT retry the same path — "
38
+
"use an allowed path/tool, or report exactly what access is needed.",
"The operation timed out / the resource is unreachable. Set it aside, route to a "
47
+
"fallback if one exists, and do NOT loop on health checks or retry blindly.",
48
+
),
49
+
(
50
+
re.compile(
51
+
r"\b(char(acter)?s?|byte)s?\b.*\b(limit|exceed|too (long|large|big)|maximum)\b|exceeds the maximum|max(imum)? (length|size)|too many tokens|context length",
52
+
re.I,
53
+
),
54
+
"limit",
55
+
"A size/length limit was hit. Don't resend as-is — chunk the work, summarize, "
56
+
"or raise the relevant config limit; a near-identical retry will fail the same way.",
57
+
),
58
+
(
59
+
re.compile(
60
+
r"\bno results\b|\bno results found\b|duckduckgo search failed|brave search returned http|could not reach .* search|searxng returned http",
61
+
re.I,
62
+
),
63
+
"provider_dead",
64
+
"The active search provider is not returning results. Switch to a different "
65
+
"search_backend in config.yaml or via hermes tools, or report the blocker "
66
+
"if no alternative provider is configured. Do not retry the same query with "
67
+
"the same provider.",
68
+
),
69
+
(
70
+
re.compile(
71
+
r"no such file or directory|not found|does not exist|cannot find|no matches found|0 results|no results",
72
+
re.I,
73
+
),
74
+
"not_found",
75
+
"The target wasn't found. Re-check the path/name (it may be dynamic), broaden the "
76
+
"search, or create the prerequisite first — don't repeat the same lookup.",
77
+
),
78
+
(
79
+
re.compile(
80
+
r"traceback \(most recent call|exit code[:\s]+[1-9]|exit status [1-9]|non-zero exit|error:|exception|failed",
81
+
re.I,
82
+
),
83
+
"runtime_error",
84
+
"The call errored. Read the message, fix the root cause, and CHANGE the call — "
0 commit comments