Skip to content

Fix SDE search: migrate to the current endpoint and honor min_score - #115

Open
pushwithak wants to merge 3 commits into
NASA-IMPACT:developfrom
pushwithak:fix/sde-search-endpoint-min-score
Open

Fix SDE search: migrate to the current endpoint and honor min_score#115
pushwithak wants to merge 3 commits into
NASA-IMPACT:developfrom
pushwithak:fix/sde-search-endpoint-min-score

Conversation

@pushwithak

@pushwithak pushwithak commented Jul 13, 2026

Copy link
Copy Markdown

Summary

sde_search_tool and repository_search_tool return zero results for many queries against the SDE API as it is deployed today, for two reasons this PR fixes:

  • Retired endpoint — both tools defaulted to an old SDE distribution. They now default to the current host (SDE_BASE_URL-overridable).
  • Missing min_score — neither tool sent min_score. The SDE endpoints apply a server-side default of 0.55 when the field is omitted, which is above the relevance score most documents receive (hybrid search scores most hits around 0.01), so the whole result set is silently dropped. Both tools now send min_score on every request, default it to 0.0, and expose it as config.

Changes

  • sde_search — searches /api/search; current-host default via a single DEFAULT_SDE_BASE_URL constant (resolved lazily so SDE_BASE_URL set after import is honored); min_score on every request; request URL is rstrip'd so a trailing-slash SDE_BASE_URL (shared with code_signals) can't produce host//api/search.
  • repository_search — searches the code-specific /api/code/search endpoint. SDE_BASE_URL is treated as a bare host (consistent with sde_search and code_signals) with /api/code/search appended per request, routed through the shared DEFAULT_SDE_BASE_URL constant; min_score=0.0 on every request. Because results now actually flow, the GitHub-enrichment path runs for the first time and is hardened alongside the fix:
    • the SDE call is async (httpx.AsyncClient, one client reused across pages) instead of blocking requests.post;
    • HTTP errors are raised (raise_for_status) and transient failures retried, instead of an error body silently becoming "no results";
    • a result URL that isn't github.com/owner/repo (an org page, another host) returns empty metadata / null reliability score instead of raising IndexError and sinking the whole query via asyncio.gather;
    • results with an invalid URL are skipped while still backfilling to the requested count, and a single enrichment failure keeps its result (empty metadata) rather than failing the whole batch.
  • utilsfirst_commit_date stays a str so repository metadata serializes cleanly (a None there failed MCP output validation once results started flowing).

Test plan

  • uv run pytest tests/tools/test_sde_search.py tests/tools/code_search/ — passes (unit + live functional)
  • Unit guards pin each tool's host default, that min_score is on every request, and that a host-form SDE_BASE_URL (with a trailing slash) produces the right endpoint URL
  • A non-owner/repo URL returns None (no IndexError) and enrichment returns empty metadata for it
  • Verified against the live endpoint: "UF universal format weather radar .uf reader python reflectivity" returns 0 results with min_score omitted vs 385 with min_score=0.0
  • MCP server registers sde_search_tool and repository_search_tool

@pushwithak
pushwithak force-pushed the fix/sde-search-endpoint-min-score branch 2 times, most recently from e09615e to 23f4f74 Compare July 13, 2026 18:01
The SDE tools point at a retired distribution and omit min_score, so
sde_search_tool and repository_search_tool return zero results for many
queries against the SDE API as deployed today. The endpoints apply a
server-side default of 0.55 for min_score when it is omitted, which is above
the score most documents receive and silently drops the whole result set.

- sde_search: default to the current SDE host (via a single DEFAULT_SDE_BASE_URL
  constant, resolved lazily) and send min_score on every /api/search request
  (config field, default 0.0). The request URL is rstripped so a trailing-slash
  SDE_BASE_URL can't double up.
- repository_search: point the code-search endpoint (/api/code/search) at the
  current host and send min_score=0.0 on every request. Because results now
  actually flow, the GitHub-enrichment path runs for the first time, so it is
  hardened: the SDE call is async and reuses a single client, HTTP errors are
  raised (and transient ones retried) instead of silently becoming empty
  results, results with invalid URLs are skipped while still backfilling to the
  requested count, a URL that is not github.com/owner/repo yields empty metadata
  instead of raising, and a single enrichment failure no longer sinks the whole
  result set.
- utils: keep first_commit_date a str so repository metadata serializes cleanly.

Verified against the live endpoint: "UF universal format weather radar .uf
reader python reflectivity" returns 0 results before and 385 after.
@pushwithak
pushwithak force-pushed the fix/sde-search-endpoint-min-score branch from 23f4f74 to 56f27e9 Compare July 13, 2026 18:20

@sanzog03 sanzog03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code review findings from a high-effort automated pass. Two correctness items (one confirmed, one plausible) and one cleanup — see inline comments.

Comment thread akd_ext/tools/code_search/repository_search.py Outdated
repository_metadata.pulls = repo.get_pulls(state="open", sort="created", base="master").totalCount
repository_metadata.closed_pulls = repo.get_pulls(state="closed", sort="created", base="master").totalCount
repository_metadata.first_commit_date = None # The original code provided also fell back to created_at if first_commit_date was not available. And it was set to None by default.
# first_commit_date is left at its "" default: GitHub doesn't expose it cheaply, and

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🟡 Correctness (plausible): dropping the explicit first_commit_date = None can flip reliability_score from 0.0 to null.

The field now stays at its declared default "" on success. Since None != "", a successfully-fetched repo can satisfy is_null_metadata where it previously never could. For a repo whose other fields also land at defaults (notably created_at == ""), is_null_metadata flips False → True, so calculate_reliability_score short-circuits to None instead of 0.0 — changing a reported reliability_score from 0.0 to null.

Worth confirming is_null_metadata's definition doesn't now include first_commit_date == "" as a trigger for the empty-default case.

@pushwithak pushwithak Jul 13, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

is_null_metadata only returns True if every field is at its default, including created_at == "". On a successful fetch created_at is always populated from repo.created_at.isoformat(), so it stays False and the score is computed (a zero-star repo scores e.g. 35.0, not null). created_at == "" only happens on the failure path, where null is correct. So no regression from leaving first_commit_date at its "" default.

@pushwithak
pushwithak force-pushed the fix/sde-search-endpoint-min-score branch from 56f27e9 to c5c3a7b Compare July 13, 2026 19:47
repository_search consumed SDE_BASE_URL as the full endpoint, while sde_search / code_signals and .env.example treat it as a bare host. Setting SDE_BASE_URL to a host therefore made repository_search POST to the bare host -> 404 -> silent zero results. It now routes the default through the shared DEFAULT_SDE_BASE_URL constant and appends /api/code/search per request (rstrip guards a trailing slash). Adds a unit test pinning host-form URL construction.
@pushwithak
pushwithak force-pushed the fix/sde-search-endpoint-min-score branch from c5c3a7b to 8452e85 Compare July 13, 2026 19:54

@sanzog03 sanzog03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Follow-up review of the fix commit. One correctness item on the new env placeholder — see inline.

Comment thread .env.example Outdated
SDE_BASE_URL="https://dyejsbdumgpqz.cloudfront.net"
# Optional. Without it, GitHub throttles at 60 requests/hour and repository_search_tool
# returns null reliability_score for repositories it could not fetch metadata for.
GITHUB_ACCESS_TOKEN="xxxxxxxxxx"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🔴 Correctness: this bogus token placeholder actively breaks scoring — it's worse than leaving the var unset.

A user who copies .env.example verbatim (the documented setup path) keeps GITHUB_ACCESS_TOKEN="xxxxxxxxxx". repository_search_tool then authenticates every GitHub call with that invalid token → PyGithub raises 401 inside fetch_github_metadata → the exception is swallowed and an empty RepositoryMetadata is returned → is_null_metadata is Truecalculate_reliability_score returns None. Every repository_search result comes back with reliability_score: null.

That's strictly worse than leaving the var unset, where unauthenticated requests (60/hr) succeed and produce real scores — and the comment just above even promises the token prevents null scores, so the placeholder misleads.

Suggest shipping it empty or commented out, e.g.:

# GITHUB_ACCESS_TOKEN=""

so an unconfigured copy falls back to working unauthenticated requests instead of failing auth.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed and fixed. Verified the mechanism: with GITHUB_ACCESS_TOKEN="xxxxxxxxxx", fetch_github_metadata builds Auth.Token("xxxxxxxxxx") → 401 → swallowed → empty metadata → reliability_score: null for every result. Unset and empty both leave auth=None and return real scores (stars=3, score=63.64 for veda-config-ghg). Commented the variable out in .env.example so a verbatim copy falls back to unauthenticated requests, and corrected the note — the previous one blamed the token's absence when it's the invalid placeholder that nulls scores.

A copied .env.example kept GITHUB_ACCESS_TOKEN="xxxxxxxxxx", which authenticates
every GitHub call with an invalid token: PyGithub raises 401, fetch_github_metadata
swallows it, and repository_search returns reliability_score: null for every
result. That is strictly worse than leaving the token unset, where unauthenticated
requests succeed and produce real scores.

Comment the variable out so a verbatim copy falls back to unauthenticated
requests, and correct the note (the previous one blamed the token's absence for
null scores; it is the invalid placeholder that nulls them).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants