File size: 4,770 Bytes
95d6832
7f94735
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95d6832
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f94735
470138f
 
 
 
 
 
 
 
7dca6d7
470138f
 
 
 
 
 
bf7f9a0
7f94735
 
470138f
 
 
7dca6d7
bf7f9a0
eed4c67
7dca6d7
eed4c67
 
 
7f94735
7dca6d7
7f94735
 
 
 
 
 
 
 
 
2f0fa95
 
 
 
 
 
 
 
 
 
 
7f94735
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95d6832
7f94735
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""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,
    }