-
-
Notifications
You must be signed in to change notification settings - Fork 6
feat(escalating): add live web tool detection and routing for requests #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import logging | ||||||||||||||||||||||
| import re | ||||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import dspy | ||||||||||||||||||||||
|
|
@@ -26,12 +27,28 @@ | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| ESCALATION_SENTINEL = "[TOOLS NEEDED]" | ||||||||||||||||||||||
| _RLM_FALLBACK_WARNING = "RLM escalation failed; returned a lightweight fallback response." | ||||||||||||||||||||||
| _LIVE_WEB_URL_RE = re.compile(r"https?://[^\s<>'\"]+", flags=re.IGNORECASE) | ||||||||||||||||||||||
| _LIVE_WEB_REQUEST_RE = re.compile( | ||||||||||||||||||||||
| r"\b(" | ||||||||||||||||||||||
| r"browse|download|fetch|open|read|retrieve|scrape|summari[sz]e" | ||||||||||||||||||||||
| r")\b.*\b(" | ||||||||||||||||||||||
| r"internet|online|page|pdf|site|url|web|website" | ||||||||||||||||||||||
| r")\b", | ||||||||||||||||||||||
| flags=re.IGNORECASE, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def _is_rlm_execution_mode(execution_mode: str) -> bool: | ||||||||||||||||||||||
| return execution_mode in {"rlm", "rlm_only"} | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def _requires_live_web_tools(user_request: str) -> bool: | ||||||||||||||||||||||
| """Return whether a turn should skip lightweight chat and use web-capable tools.""" | ||||||||||||||||||||||
| if _LIVE_WEB_URL_RE.search(user_request): | ||||||||||||||||||||||
| return True | ||||||||||||||||||||||
| return bool(_LIVE_WEB_REQUEST_RE.search(user_request)) | ||||||||||||||||||||||
|
Comment on lines
+45
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def _history_value(message: Any, *keys: str) -> str: | ||||||||||||||||||||||
| if isinstance(message, dict): | ||||||||||||||||||||||
| for key in keys: | ||||||||||||||||||||||
|
|
@@ -191,6 +208,14 @@ def forward( | |||||||||||||||||||||
| history=history, | ||||||||||||||||||||||
| conversation_summary=conversation_summary, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| if _requires_live_web_tools(user_request): | ||||||||||||||||||||||
| logger.debug("EscalatingFleetModule: routing live-web request to RLM path") | ||||||||||||||||||||||
| return self._run_rlm( | ||||||||||||||||||||||
| user_request=user_request, | ||||||||||||||||||||||
| core_memory=core_memory, | ||||||||||||||||||||||
| history=history, | ||||||||||||||||||||||
| conversation_summary=conversation_summary, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| prediction = self.respond( | ||||||||||||||||||||||
| user_request=user_request, | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -69,6 +69,19 @@ def test_rlm_path_triggered_by_sentinel_in_reasoning(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module._rlm.assert_called_once() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert getattr(result, "answer", None) == "deep answer" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_url_fetch_request_forces_rlm_before_lightweight_response(self) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module = _make_module() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _stub_respond(module, reasoning="I cannot browse the live web.", response="no web access") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rlm_pred = _FakePrediction(answer="fetched document") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module._rlm = MagicMock(return_value=rlm_pred) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _stub_summarize(module) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = module(user_request="fetch https://arxiv.org/pdf/2512.24601 please", execution_mode="auto") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module.respond.assert_not_called() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module._rlm.assert_called_once() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert getattr(result, "answer", None) == "fetched document" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+72
to
+83
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test only verifies routing when a URL is present in the request (which matches
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_force_escalate_skips_cot(self) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module = _make_module() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _stub_respond(module) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current regex
_LIVE_WEB_REQUEST_REenforces a strict order where the action verb (e.g.,browse) must precede the target noun (e.g.,website) due to the.*pattern. This means queries like"Is there a website I can browse?"or"pdf to summarize"will fail to match, even though they clearly require live web tools.Additionally, using
.*can introduce backtracking overhead on long inputs.We can resolve this by splitting the pattern into two separate, simpler regexes for actions and targets, making the check completely order-independent and more efficient.