Skip to content
Merged
21 changes: 19 additions & 2 deletions backend/utils/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def get_tts_semaphore() -> asyncio.Semaphore:
_auth_client: httpx.AsyncClient | None = None
_stt_client: httpx.AsyncClient | None = None
_tts_client: httpx.AsyncClient | None = None
_web_fetch_client: httpx.AsyncClient | None = None


def get_webhook_client() -> httpx.AsyncClient:
Expand Down Expand Up @@ -299,10 +300,25 @@ def get_tts_client() -> httpx.AsyncClient:
return _tts_client


def get_web_fetch_client() -> httpx.AsyncClient:
"""Return a shared async HTTP client for user-initiated URL fetches.

Isolated from the webhook pool so slow/stalled external pages don't
compete with partner webhook delivery slots.
"""
global _web_fetch_client
if _web_fetch_client is None:
_web_fetch_client = httpx.AsyncClient(
timeout=httpx.Timeout(15.0, connect=5.0),
limits=httpx.Limits(max_connections=16, max_keepalive_connections=4),
)
return _web_fetch_client


async def close_all_clients():
"""Close all shared HTTP clients. Call at app shutdown."""
global _webhook_client, _maps_client, _auth_client, _stt_client, _tts_client
for client in (_webhook_client, _maps_client, _auth_client, _stt_client, _tts_client):
global _webhook_client, _maps_client, _auth_client, _stt_client, _tts_client, _web_fetch_client
for client in (_webhook_client, _maps_client, _auth_client, _stt_client, _tts_client, _web_fetch_client):
if client is not None:
try:
await client.aclose()
Expand All @@ -313,6 +329,7 @@ async def close_all_clients():
_auth_client = None
_tts_client = None
_stt_client = None
_web_fetch_client = None
# Reset stateful registries
_semaphores.clear()
_webhook_circuit_breakers.clear()
Expand Down
11 changes: 11 additions & 0 deletions backend/utils/retrieval/agentic.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
get_screen_activity_tool,
search_screen_activity_tool,
save_user_preference_tool,
fetch_url_tool,
)
from utils.retrieval.tools.app_tools import load_app_tools, get_tool_status_message
from utils.retrieval.safety import AgentSafetyGuard, SafetyGuardError
Expand Down Expand Up @@ -96,6 +97,7 @@ def decorator(func):
get_screen_activity_tool,
search_screen_activity_tool,
save_user_preference_tool,
fetch_url_tool,
]

# Standard tool names (used to detect app tools by exclusion)
Expand Down Expand Up @@ -133,6 +135,7 @@ def get_tool_display_name(tool_name: str, tool_obj: Optional[Any] = None) -> str
'get_screen_activity_tool': 'Checking screen activity',
'search_screen_activity_tool': 'Searching screen activity',
'save_user_preference_tool': 'Saving preference',
'fetch_url_tool': 'Reading page',
}

if tool_name in tool_display_map:
Expand Down Expand Up @@ -561,6 +564,14 @@ async def execute_agentic_chat_stream(
IMPORTANT: Always search for and use these tools when relevant. Never tell the user you don't have access to an integration if a matching tool exists above.
</available_app_tools>"""

# Instruct Claude to use fetch_url_tool for any direct URL in the conversation.
# Without this, Claude's built-in "I can't browse links" behavior takes over.
system_prompt += """

<url_fetching_instructions>
You have fetch_url_tool available. When the user shares any URL (starting with http:// or https://), you MUST call fetch_url_tool to read its content before responding. Never say you cannot browse, visit, or read a URL. Always attempt to fetch it first.
</url_fetching_instructions>"""

# Convert tools to Anthropic format (core = visible, app = defer_loading)
tool_schemas, tool_registry = _convert_tools(core_tools, app_tools)

Expand Down
4 changes: 4 additions & 0 deletions backend/utils/retrieval/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
from .preference_tools import (
save_user_preference_tool,
)
from .web_tools import (
fetch_url_tool,
)

__all__ = [
'get_conversations_tool',
Expand All @@ -79,4 +82,5 @@
'get_screen_activity_tool',
'search_screen_activity_tool',
'save_user_preference_tool',
'fetch_url_tool',
]
Loading
Loading