Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .agents/skills/do-web-doc-resolver/scripts/cache_negative.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
Negative caching logic for the Web Doc Resolver.
"""

import logging
from datetime import datetime, timedelta, timezone

logger = logging.getLogger(__name__)


def should_skip_from_negative_cache(cache, key: str, provider: str) -> bool:
if cache is None:
Expand All @@ -24,6 +27,7 @@ def should_skip_from_negative_cache(cache, key: str, provider: str) -> bool:
dt = dt.replace(tzinfo=timezone.utc)
return dt > datetime.now(timezone.utc)
except Exception:
logger.debug("Failed to parse negative cache expiry: %s", expires_at, exc_info=True)
return False


Expand All @@ -49,3 +53,19 @@ def write_negative_cache(
"metadata": metadata,
}
cache.set(f"neg:{provider}:{key}", entry, expire=ttl_seconds)


def should_skip_from_bot_challenge_cache(
provider: str,
url: str,
bot_challenge_cache: dict[str, set[str]],
) -> bool:
"""Skip plain-fetch providers for URLs known to serve bot challenges."""
from urllib.parse import urlparse

try:
domain = urlparse(url).netloc
return provider in ("direct_fetch",) and domain in bot_challenge_cache.get(provider, set())
except Exception as e:
logger.debug("Bot challenge cache lookup failed for %s: %s", url, e)
return False
18 changes: 18 additions & 0 deletions .agents/skills/do-web-doc-resolver/scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
import os
import typing

from scripts.models import FetchTier

logger = logging.getLogger(__name__)

if typing.TYPE_CHECKING:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Body doesn't contain any code


In most cases, an empty body of for, while or if implies some piece of code is missing.
Such empty block must be either filled or removed.

pass


def _load_config() -> dict[str, typing.Any]:
config_path = os.getenv("DO_WDR_CONFIG") or "config.toml"
Expand Down Expand Up @@ -110,3 +115,16 @@ def _env(
BLOCKED_SCHEMES: set[str] = {"file", "javascript", "data", "vbscript"}

DNS_CACHE_TTL: int = 60

CLEAN_CONTENT: bool = os.environ.get("WDR_CLEAN_CONTENT", "1") != "0"

PROVIDER_TIERS: dict[str, FetchTier] = {
"llms_txt": FetchTier.FREE_STATIC,
"direct_fetch": FetchTier.FREE_DIRECT,
"duckduckgo": FetchTier.FREE_SEARCH,
"jina": FetchTier.PAID_LITE,
"firecrawl": FetchTier.PAID_LITE,
"visual_clip": FetchTier.PAID_LITE,
"stealth": FetchTier.STEALTH,
"mistral_browser": FetchTier.PAID_BROWSER,
}
27 changes: 27 additions & 0 deletions .agents/skills/do-web-doc-resolver/scripts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ErrorType(Enum):
INVALID_RESPONSE = "invalid_response"
SSRF_BLOCKED = "ssrf_blocked"
CONTENT_TOO_LARGE = "content_too_large"
BOT_CHALLENGE = "bot_challenge"
UNKNOWN = "unknown"


Expand Down Expand Up @@ -50,6 +51,18 @@ def max_hops(self) -> int:
return 4


class FetchTier(int, Enum):
"""Escalation cost tier for fetch providers.
Lower = cheaper, always tried first."""

FREE_STATIC = 0 # llms_txt: static text file, zero cost
FREE_DIRECT = 1 # direct_fetch: plain httpx, zero cost
FREE_SEARCH = 2 # duckduckgo: free web search
PAID_LITE = 3 # jina, firecrawl: paid but cheap per-call
STEALTH = 4 # anti-bot bypass tier
PAID_BROWSER = 5 # mistral_browser: paid + JS execution


class ProviderType(Enum):
"""Available providers for resolution."""

Expand All @@ -71,6 +84,7 @@ class ProviderType(Enum):
# New providers
DOCLING = "docling"
OCR = "ocr"
VISUAL_CLIP = "visual_clip"

def is_paid(self) -> bool:
return self in (
Expand All @@ -80,6 +94,7 @@ def is_paid(self) -> bool:
ProviderType.FIRECRAWL,
ProviderType.MISTRAL_WEBSEARCH,
ProviderType.MISTRAL_BROWSER,
ProviderType.VISUAL_CLIP,
)

def is_fast(self) -> bool:
Expand Down Expand Up @@ -179,3 +194,15 @@ class ReadonlyResolverProtocol(Protocol):
"""

def __call__(self) -> ResolvedResult | str | None: ...


__all__ = [
"ErrorType",
"Profile",
"ProviderType",
"ValidationResult",
"ProviderMetric",
"ResolveMetrics",
"ResolvedResult",
"ReadonlyResolverProtocol",
]
Loading
Loading