Conversation
Treat bare JSON arrays as native parameter values and require $choices for discrete candidate expansion. Migrate built-in proposals, examples, tests, and the English and Chinese contracts. Signed-off-by: jc543239 <jc543239@antgroup.com> Assisted-by: Codex:GPT-5
|
/label status/waiting-for-review |
Merge Protections🟢 All 2 merge protections satisfied — ready to merge. Show 2 satisfied protections🟢 Require kind label
🟢 Require version label
|
There was a problem hiding this comment.
Pull request overview
This PR refactors the experimental AutoTune JSON candidate-expression grammar to remove ambiguity: bare JSON arrays are now treated as literal native parameter values, while discrete candidate expansion requires an explicit {"$choices":[...]} expression (with validation and deduplication), preserving existing $range behavior. It migrates built-in proposals, examples, tests, and the English/Chinese AutoTune docs to the new explicit syntax.
Changes:
- Update candidate expansion to treat arrays as concrete values and add explicit
$choicesexpansion with validation + duplicate removal. - Migrate built-in proposal generation, examples, and C++ regression tests to the
$choicessyntax. - Update English/Chinese AutoTune documentation and API contract to match the unambiguous grammar.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tools/autotune/autotune_candidate.cpp | Implements explicit $choices expansion/validation and changes array handling to literal pass-through. |
| tools/autotune/autotune_test.cpp | Migrates tests to $choices and adds regression coverage for literal arrays vs choices + validation rules. |
| tools/autotune/autotune.h | Updates public header comments to describe the updated parameter-space grammar. |
| tools/autotune/examples/sift_hgraph_autotune_request.json | Migrates example request from bare arrays to explicit $choices. |
| tools/autotune/examples/README.md | Updates example documentation to reference $choices and clarify bare-array behavior. |
| examples/cpp/326_feature_create_index_with_constraints.cpp | Updates sample code to use $choices for discrete search candidates. |
| examples/cpp/327_feature_autotune_existing_index.cpp | Updates sample code to use $choices for discrete search candidates. |
| examples/cpp/328_feature_autotune_existing_pyramid.cpp | Updates sample code to use $choices for discrete search candidates. |
| docs/docs/en/src/resources/autotune.md | Syncs user guide with explicit $choices and literal array semantics. |
| docs/docs/en/src/resources/autotune_api_v1.md | Updates API contract to define $choices and literal arrays unambiguously. |
| docs/docs/zh/src/resources/autotune.md | Syncs Chinese user guide with explicit $choices and literal array semantics. |
| docs/docs/zh/src/resources/autotune_api_v1.md | Updates Chinese API contract to define $choices and literal arrays unambiguously. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
LHT129
left a comment
There was a problem hiding this comment.
Overall this is a clean refactor that makes the AutoTune candidate grammar unambiguous. The code changes are well-tested with comprehensive validation coverage. I have one suggestion for the SearchRequest::parameter_space comment to align it with the IndexSpace documentation.
[suggestion] tools/autotune/autotune.h:106 — The SearchRequest::parameter_space comment mentions only $choices and $range, but the new grammar also allows concrete JSON values (including bare arrays) to pass through unchanged. The IndexSpace comments in the same header correctly describe this. Consider aligning this comment to avoid confusing API consumers.
Current:
/// JSON search space with explicit $choices or $range candidate expressions; missing supported
/// fields receive built-in proposals.
Suggested:
/// JSON search space with concrete values, explicit $choices, or $range candidate expressions;
/// missing supported fields receive built-in proposals. Bare arrays are concrete values.
| Workload workload; | ||
| /// JSON search candidate space; missing supported fields receive built-in proposals. | ||
| /// JSON search space with explicit $choices or $range candidate expressions; missing supported | ||
| /// fields receive built-in proposals. |
There was a problem hiding this comment.
[suggestion] The SearchRequest::parameter_space comment mentions only $choices and $range, but the new grammar also allows concrete JSON values (including bare arrays) to pass through unchanged. The IndexSpace comments in the same header correctly describe this. Consider aligning this comment with the IndexSpace documentation to avoid confusing API consumers.
| Workload workload; | ||
| /// JSON search candidate space; missing supported fields receive built-in proposals. | ||
| /// JSON search space with explicit $choices or $range candidate expressions; missing supported | ||
| /// fields receive built-in proposals. |
There was a problem hiding this comment.
[suggestion] The SearchRequest::parameter_space comment mentions only $choices and $range, but the new grammar also allows concrete JSON values (including bare arrays) to pass through unchanged. The IndexSpace comments in the same header correctly describe this. Consider aligning this comment with the IndexSpace documentation to avoid confusing API consumers.
Suggested:
/// JSON search space with concrete values, explicit $choices, or $range candidate expressions;
/// missing supported fields receive built-in proposals.|
|
||
| std::set<std::string> seen; | ||
| for (const auto& choice : choices) { | ||
| if (seen.emplace(choice.dump()).second) { |
There was a problem hiding this comment.
[note] The expand_choices function uses choice.dump() for deduplication, which serializes each candidate value to a string on every iteration. This is correct and handles arbitrary JSON types uniformly, but it means every candidate — even simple integers — incurs a full JSON serialization. Since candidate counts are typically small (bounded by max_trials), this is unlikely to be a practical concern. Just noting for awareness.
| const auto second = std::min<uint64_t>(2048, base_count); | ||
| params["buckets_count"] = | ||
| first == second ? JsonType(first) : JsonType::array({first, second}); | ||
| first == second ? JsonType(first) : candidate_choices(JsonType::array({first, second})); |
There was a problem hiding this comment.
[note] When first == second (i.e., base_count <= 1024), buckets_count is set to a plain scalar instead of {"$choices": [...]}. This is correct — a single value doesn't need candidate expansion — but it means the field's JSON type differs depending on the dataset size. This is fine since the expand() function handles scalars, objects, and arrays uniformly. Just noting that callers inspecting the raw JSON shouldn't assume buckets_count is always wrapped in $choices.
LHT129
left a comment
There was a problem hiding this comment.
Thanks for this PR. The $choices syntax is a clean improvement over the implicit bare-array-as-candidates behavior — it makes the grammar unambiguous and properly separates "literal native parameter value" from "discrete candidate expansion."
Summary of changes:
expand()now treats bare arrays as literal values (pass-through), and only$choices/$rangetrigger candidate expansion.- New
expand_choices()function with validation (non-empty array, no mixed keys) and duplicate removal viachoice.dump(). - Built-in proposals (
fill_*functions) now emit$choiceswrappers instead of bare arrays. - Tests, examples, and docs updated consistently.
Review comments posted:
autotune.h:106—SearchRequest::parameter_spacecomment should mention concrete values (not just$choices/$range), matchingIndexSpace.autotune_candidate.cpp:167—choice.dump()for dedup is correct but worth noting as a serialization cost per element.autotune_candidate.cpp:238—fill_ivf_createscalar fallback whenfirst == secondis correct but callers shouldn't assumebuckets_countis always wrapped.
All three are [note]/[suggestion] level — no blocking issues found. The tests are thorough (validation, dedup, native array pass-through, array-of-arrays choices).
|
|
||
| std::set<std::string> seen; | ||
| for (const auto& choice : choices) { | ||
| if (seen.emplace(choice.dump()).second) { |
There was a problem hiding this comment.
[suggestion] The choice.dump() deduplication relies on nlohmann::json::dump() preserving key insertion order. Two semantically identical JSON objects with different key insertion orders would produce different serialized strings and thus NOT be deduplicated, potentially yielding duplicate candidates.
For example:
{"$choices": [{"a": 1, "b": 2}, {"b": 2, "a": 1}]}would produce two candidates with identical semantics.
In practice, most $choices items are scalars (integers/strings) where this is not an issue, and object-valued choices typically come from a single construction path with deterministic ordering. However, if users ever construct $choices with object items from different sources, duplicates could leak through.
Consider sorting keys before serialization, or using a canonicalization approach for object-valued choices.
LHT129
left a comment
There was a problem hiding this comment.
Reviewed the full diff and all core source files. This is a clean refactor that makes the AutoTune candidate grammar unambiguous.
Summary of changes:
expand()now treats bare arrays as literal pass-through values; only$choices/$rangetrigger candidate expansion.- New
expand_choices()with validation (non-empty array, no mixed keys) and duplicate removal viachoice.dump(). - Built-in proposals (
fill_*functions) emit$choiceswrappers via thecandidate_choices()helper. - Tests, examples, and English/Chinese docs updated consistently with thorough coverage (validation, dedup, native array pass-through, array-of-arrays choices).
Review comments already posted by other reviewers cover:
autotune.h:106—SearchRequest::parameter_spacecomment should mention concrete values alongside$choices/$range, matchingIndexSpacedocumentation.autotune_candidate.cpp:167—choice.dump()for dedup incurs per-element serialization cost (acceptable given small candidate counts).autotune_candidate.cpp:167—choice.dump()may not deduplicate semantically identical objects with different key insertion orders.autotune_candidate.cpp:238—fill_ivf_createscalar fallback whenfirst == secondproduces a type-inconsistentbuckets_countfield.
No additional blocking issues found. The test coverage is thorough (265 assertions in 26 test cases), and the migration is complete across all built-in proposals, examples, and documentation.
LHT129
left a comment
There was a problem hiding this comment.
I have completed a thorough review of this PR. The existing 6 inline comments from LHT129 and Copilot already cover the substantive findings:
-
SearchRequest::parameter_spacecomment alignment (LHT129): The comment should mention concrete values alongside$choices/$range, matching theIndexSpacecomments. A suggested fix was provided. -
choice.dump()deduplication performance (LHT129): Serializing every candidate for dedup is correct but noted for awareness — not a practical concern given small candidate counts. -
choice.dump()key-ordering edge case (LHT129): Object-valued$choicesitems with different key insertion orders would not be deduplicated. This is a corner case unlikely in practice but worth addressing for robustness. -
buckets_countscalar vs$choicesinconsistency (LHT129): Whenfirst == second, the field is a plain scalar instead of{"$choices": [...]}. This is correct behavior but noted for callers inspecting raw JSON.
My assessment:
- Core logic (
expand(),expand_choices(),candidate_choices()): Clean and correct. The separation of bare arrays (concrete values) from$choices(candidate expansion) is well-implemented. - Validation: Good coverage — empty
$choices, non-array$choices, mixed keys, and duplicate removal are all validated with clear error messages. - Tests: Two new test cases (
AutoTune distinguishes native arrays from explicit choices,AutoTune validates explicit choices) plus updates to existing tests. The tests cover bare array passthrough, empty array passthrough,$choiceswith array-valued items, validation error paths, and deduplication. - Documentation: All 4 doc files (EN/ZH autotune.md and autotune_api_v1.md) are consistently updated with the new
$choicessyntax and bare-array semantics. - Examples: All 3 C++ examples and the JSON example are updated to use the new syntax.
- Backward compatibility: This is a breaking change for the experimental AutoTune API, but the PR description acknowledges this and the change is intentional before broader adoption.
No new blocking issues found beyond what is already covered by the existing comments. The PR is well-structured and the test evidence in the PR description is thorough.
LHT129
left a comment
There was a problem hiding this comment.
I have reviewed this PR thoroughly. The refactoring is well-designed and the implementation is clean.
Summary of changes:
- Bare JSON arrays are now treated as native index parameter values (passed through unchanged).
- Explicit
{"$choices": [...]}syntax is introduced for discrete candidate expansion. - All built-in proposals, examples, tests, and documentation are migrated to the new syntax.
- Validation is added:
$choicesmust be an array, non-empty, and not mixed with other keys. - Duplicate removal is handled via
choice.dump()serialization.
What I checked:
- The
expand()function correctly distinguishes bare arrays (emit directly) from$choices/$rangeobjects (expand). expand_object()recursion still works correctly for nested objects containing$choices/$range.- The
candidate_choices()helper consistently wraps all built-in proposals. - Test coverage is thorough: native array passthrough,
$choicesvalidation, duplicate removal, and migration of existing tests. - Documentation (EN/ZH) accurately reflects the new grammar.
No blocking issues found. The existing inline comments from other reviewers about the SearchRequest::parameter_space docstring and choice.dump() deduplication edge cases are reasonable suggestions but non-blocking.
This is a clean, well-tested refactor that makes the AutoTune grammar unambiguous. Nice work.
Change Type
Linked Issue
What Changed
{"$choices": [...]}candidate expansion with validation, atomic alternatives,and duplicate removal while preserving existing
$rangebehavior.AutoTune documentation to the unambiguous syntax.
Test Evidence
make fmtmake lintmake testmake cov, run tests, and collect coverageTest details:
Compatibility Impact
$choices. This intentionally replaces the ambiguous experimental grammar before adoption.Performance and Concurrency Impact
Documentation Impact
README.mdDEVELOPMENT.mdCONTRIBUTING.mdRisk and Rollback
Checklist
kind/bugandkind/feature; see "Linked Issue" above)[skip ci]prefix)