"""Deterministic detection of e-resource access problems. Access problems ("I can't access ScienceDirect", "OpenAthens login keeps failing", "how do I access databases off campus") are routed straight to the knowledge-base RAG path. Requests for a specific article's full text stay on the full-text chain. Kept separate from constants.py so it has no dependencies and can be tested on its own. """ import re # Unambiguous access/authentication cues. Context-dependent words (remote, # VPN, off campus) only count next to an access or sign-in verb, so research # topics such as "remote sensing" or "VPN security" are not misrouted. ACCESS_PROBLEM_RE = re.compile( r"\b(" r"openathens|single sign.?on" r"|access (denied|error|issue|problem)s?" r"|(asks?|asking|wants?) me to (pay|purchase|buy|subscribe)" r"|(log(?:ging)?\s?(?:in|on)|sign(?:ing)?\s?in|sso)\W+(?:\w+\W+){0,4}(fail\w*|error|problem|issue|loop\w*|not working|keeps)" r"|(off[\s-]?campus|remote(ly)?|from home|outside (the )?campus|vpn)\W+(?:\w+\W+){0,4}(access\w*|log(?:ging)?\s?(?:in|on)|sign(?:ing)?\s?in)" r"|(access\w*|log(?:ging)?\s?(?:in|on)|sign(?:ing)?\s?in)\W+(?:\w+\W+){0,4}(off[\s-]?campus|remote(ly)?|from home|outside (the )?campus)" r"|(link|database|platform|e-?resource)s?\W+(?:\w+\W+){0,3}(not working|broken|down|dead)" r")\b", re.IGNORECASE, ) # "can't access X", "trouble logging in to X", "problem opening X" ACCESS_FAIL_RE = re.compile( r"\b(can.?t|cannot|unable to|not able to|couldn.?t|trouble|problems?|issues?|lost)\b" r"\W+(?:\w+\W+){0,3}(access\w*|log(?:ging)?\s?(?:in|on)|sign(?:ing)?\s?in|connect\w*|open\w*)\b", re.IGNORECASE, ) ARTICLE_CUE_RE = re.compile(r"\b(pdf|full.?text|articles?|papers?|chapters?)\b", re.IGNORECASE) # Extra tokens appended to the retrieval query so BM25 reliably surfaces the # access-troubleshooting and remote-access chunks alongside any resource chunk. ACCESS_RETRIEVAL_HINT = "access troubleshooting sign in remote access electronic resources" def _is_access_problem(question: str) -> bool: q = question or "" if ACCESS_PROBLEM_RE.search(q): return True # "can't access ScienceDirect" is a platform problem; # "can't access this article" is a full-text request. return bool(ACCESS_FAIL_RE.search(q)) and not ARTICLE_CUE_RE.search(q)