feat: searchControls walkthrough in init and fix spurious diff noise#11
Merged
Conversation
Add optional searchControls prompts to `algolia-agent init` covering all five supported fields (hitsPerPage, page, attributesToRetrieve, facets, responseFields). Fix `_diff()` to only compare keys present in the new payload, ignoring API-expanded defaults that caused a spurious change on every dry-run. Update README to document all searchControls fields. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR enhances the CLI’s init workflow by adding an optional interactive walkthrough for configuring searchControls, and adjusts _diff() to reduce dry-run noise caused by the API returning expanded/default searchControls fields. It also updates documentation and tests to reflect the new prompts and diff behavior.
Changes:
- Add an optional
searchControlsprompt flow toalgolia-agent init(supportshitsPerPage,page,attributesToRetrieve,facets,responseFields). - Update
_diff()to compare onlysearchControlskeys present in the new payload to avoid false-positive diffs from API-expanded defaults. - Update README examples and extend tests to cover the new init prompts and diff behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/algolia_agent/cli.py |
Adds searchControls init walkthrough and changes _diff() searchControls comparison to ignore API-added default keys. |
tests/test_cli.py |
Updates init-input fixtures and adds tests for the searchControls init flow and expanded-API diff behavior. |
README.md |
Documents the new init prompts and expands the searchControls field table. |
Comments suppressed due to low confidence (1)
src/algolia_agent/cli.py:327
_diff()filters the current agent'ssearchControlsdown tonew_sc_keys. When the new payload has an explicitly emptysearchControlsobject (or omits it entirely),new_sc_keysbecomes empty andcurr_sc_mapis forced to{}for every index. That makes_diff()unable to report a real change where an existing non-emptysearchControlswould be cleared/removed. Consider tracking presence separately (missing vs{}vsNone) and/or comparing per-index key sets instead of a global union so empty payloads can still surface a change when appropriate.
# Only compare keys present in the new payload — the API expands searchControls
# with default fields (query, page, responseFields, etc.) that we never sent,
# which would otherwise cause a spurious diff on every dry-run.
new_sc_keys = {
k
for t in new_payload.get("tools", [])
for i in t.get("indices", [])
for k in (i.get("searchControls") or {})
}
curr_sc_map = {
i["index"]: {k: v for k, v in (i.get("searchControls") or {}).items() if k in new_sc_keys}
for t in current.get("tools", [])
for i in t.get("indices", [])
}
new_sc_map = {
i["index"]: (i.get("searchControls") or {})
for t in new_payload.get("tools", [])
for i in t.get("indices", [])
}
if curr_sc_map != new_sc_map:
curr_repr = json.dumps(next(iter(curr_sc_map.values()), None))
new_repr = json.dumps(next(iter(new_sc_map.values()), None))
lines.append(f" searchControls: {curr_repr} → {new_repr}")
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+648
to
+661
| max_hits = _ask(" Cap hitsPerPage? Enter max (or leave blank to skip)") | ||
| if max_hits: | ||
| try: | ||
| n = int(max_hits) | ||
| search_controls["hitsPerPage"] = {"exposed": False, "default": n, "constraint": {"max": n}} | ||
| except ValueError: | ||
| pass | ||
| max_page = _ask(" Cap page? Enter max (or leave blank to skip)") | ||
| if max_page: | ||
| try: | ||
| n = int(max_page) | ||
| search_controls["page"] = {"exposed": False, "default": 0, "constraint": {"max": n}} | ||
| except ValueError: | ||
| pass |
Comment on lines
+908
to
+949
| def test_diff_no_false_positive_when_api_expands_search_controls(): | ||
| """API-added fields (query, page, responseFields, etc.) do not cause a spurious diff.""" | ||
| sc_config = {"hitsPerPage": {"exposed": False, "default": 10, "constraint": {"max": 10}}} | ||
| # The API returns this config plus extra default fields we never sent | ||
| sc_api = { | ||
| "hitsPerPage": {"exposed": False, "default": 10, "constraint": {"max": 10}}, | ||
| "query": {"exposed": True, "default": None}, | ||
| "page": {"exposed": True, "default": 0}, | ||
| "responseFields": {"exposed": False, "default": None}, | ||
| "facets": {"exposed": False, "default": None}, | ||
| "custom": None, | ||
| } | ||
| current = { | ||
| "name": "My Agent", "model": "gemini-2.5-flash", "instructions": "Hello.", | ||
| "tools": [{"type": "algolia_search_index", "indices": [{"index": "products", "searchControls": sc_api}]}], | ||
| } | ||
| new_payload = { | ||
| "name": "My Agent", "model": "gemini-2.5-flash", "instructions": "Hello.", | ||
| "tools": [{"type": "algolia_search_index", "indices": [{"index": "products", "searchControls": sc_config}]}], | ||
| } | ||
| assert not _diff(current, new_payload) | ||
|
|
||
|
|
||
| def test_diff_detects_change_despite_api_expansion(): | ||
| """A real change to a config-specified field is still reported even when the API expanded the object.""" | ||
| sc_api = { | ||
| "hitsPerPage": {"exposed": False, "default": 10, "constraint": {"max": 10}}, | ||
| "query": {"exposed": True, "default": None}, | ||
| "page": {"exposed": True, "default": 0}, | ||
| } | ||
| sc_new = {"hitsPerPage": {"exposed": False, "default": 5, "constraint": {"max": 5}}} | ||
| current = { | ||
| "name": "My Agent", "model": "gemini-2.5-flash", "instructions": "Hello.", | ||
| "tools": [{"type": "algolia_search_index", "indices": [{"index": "products", "searchControls": sc_api}]}], | ||
| } | ||
| new_payload = { | ||
| "name": "My Agent", "model": "gemini-2.5-flash", "instructions": "Hello.", | ||
| "tools": [{"type": "algolia_search_index", "indices": [{"index": "products", "searchControls": sc_new}]}], | ||
| } | ||
| changes = _diff(current, new_payload) | ||
| assert len(changes) == 1 | ||
| assert "searchControls" in changes[0] |
… clearing
Re-prompt instead of silently dropping hitsPerPage/page caps when the user
enters a non-integer. Fix _diff() to detect when searchControls is explicitly
cleared (searchControls: {}) by bypassing key-filtering in that branch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
algolia-agent init: adds an optionalsearchControlswalkthrough after the replica prompts, covering all five supported fields (hitsPerPage,page,attributesToRetrieve,facets,responseFields) — each skippable by leaving blank_diff(): fixes spurious changes on every dry-run caused by the API expandingsearchControlswith default fields (query,page,responseFields, etc.) that were never in the original config — now only compares keys present in the new payloadsearchControlsfield table to list all five fields and updates theinitexample to show the new promptsTest plan
pytest tests/test_cli.py -k init— 13 tests includingtest_init_with_search_controlsandtest_init_skip_search_controlspytest tests/test_cli.py -k diff— 5 tests includingtest_diff_no_false_positive_when_api_expands_search_controlsandtest_diff_detects_change_despite_api_expansionpytest tests/test_cli.py— full suite (49 tests)🤖 Generated with Claude Code