torchdocs-agent / agent /tools.py
eliezer avihail
cleanup: remove the translation module β€” the guard is English-only now (#105)
7dca6d7 unverified
Raw
History Blame Contribute Delete
4.77 kB
"""The three tools the agent loop drives.
- search_docs: hybrid retrieval + hydrate β†’ doc sections (repeatable)
- read_page: whole-page hydrate (outline-first for oversized pages)
- ask_source: refer to the real source (GitHub / DeepWiki) β€” never fabricates
Each returns a plain dict the planner LLM sees. ask_source is referral-only by
design: it points at where the implementation lives rather than claiming to
know it, so it works with no network access.
"""
from __future__ import annotations
from urllib.parse import quote
DEEPWIKI_URL = "https://deepwiki.com/pytorch/pytorch"
GH_CODE_SEARCH = "https://github.com/search?q=repo%3Apytorch%2Fpytorch+{q}&type=code"
# dropped from the GitHub code-search query: they add no signal and crowd out
# the discriminating terms (a code-search URL has a practical length limit)
_STOPWORDS = frozenset(
"a an the is are was were be to of in on for how do does did i you it "
"what when where why which that this with and or from can could should "
"my me use using implement implemented implementation work works".split()
)
_MAX_SEARCH_TERMS = 12 # keep the URL sane while retaining the meaningful words
def _search_terms(question: str) -> str:
"""Meaningful words from the question, URL-encoded for GitHub code search.
Drops stopwords (keeping code identifiers like torch.nn.Linear verbatim)
and caps the count so the URL stays sane β€” without throwing away the words
that discriminate the query (the old code kept only the first 6 words,
which dropped the actual subject of longer questions).
"""
words = [w for w in question.split() if w.lower().strip("?.,:;()") not in _STOPWORDS]
kept = (words or question.split())[:_MAX_SEARCH_TERMS]
return quote(" ".join(kept))
# content spaces the planner may restrict a search to (must match ingest's
# page_kind values); anything else from the model is ignored, not an error
SEARCH_KINDS = frozenset({"api", "tutorial", "guide"})
def search_docs(
query: str, library: str | None = None, kind: str | None = None, k: int = 8
) -> dict:
"""Hybrid docs search over the English-only index.
`kind` lets the planner choose the content space: 'api' searches only the
reference pages (catalog questions β€” "what loss functions exist?"),
'tutorial'/'guide' only the walkthroughs. Unknown values degrade to an
unrestricted search rather than failing the tool call.
"""
from index.hydrate import hydrate_sections
from index.retrieve import retrieve
if kind is not None and kind not in SEARCH_KINDS:
print(f"[search_docs] ignoring unknown kind {kind!r}", flush=True)
kind = None
pointers = retrieve(query, k=k, library=library, kind=kind)
sections = hydrate_sections(pointers) # concurrent β€” each is a live fetch on the Space
print(
f"[search_docs] {query!r} (kind={kind}) β†’ {len(pointers)} pointers, "
f"{len(sections)} hydrated",
flush=True,
)
return {
"query": query,
"sections": sections,
"titles": [s.get("heading_path", "") or s["url"] for s in sections],
}
def read_page(url: str) -> dict:
"""Whole page for a URL already surfaced by search_docs."""
from index.hydrate import hydrate_page
url = (url or "").strip()
if not url.startswith(("http://", "https://")):
# the planner sometimes passes a section HEADING it saw in a search
# result (e.g. "Build the Neural Network > Define the Class") instead of
# the url. Don't fetch that (it 'No scheme supplied'-errors and wastes a
# call) β€” tell the model exactly what read_page needs so it self-corrects.
return {
"url": url,
"error": "read_page needs the full https:// URL from a search_docs "
"result's `url` field, not a section title.",
}
page = hydrate_page(url)
if page is None:
return {"url": url, "error": "page not in the snapshot"}
return page
def ask_source(question: str) -> dict:
"""Refer a source/implementation question to the real code.
Referral-only: returns DeepWiki + GitHub code-search links for
pytorch/pytorch. Never returns claims about the code β€” the answer layer
must present these as 'look here', not as docs-cited fact.
"""
from agent.schemas import Referral
terms = _search_terms(question)
referrals = [
Referral(url=DEEPWIKI_URL, reason="AI wiki / Q&A over the pytorch/pytorch source"),
Referral(url=GH_CODE_SEARCH.format(q=terms), reason="search the implementation on GitHub"),
]
return {
"note": "Implementation lives in the source, not the docs β€” refer the user out.",
"referrals": referrals,
}