mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
fix(auto-improve): drop model-supplied expected_base_body_sha256
The field is server-owned and set when a patch is materialized, but the schema shows it to the model. A non-hash value on a full-page proposal reached staging and failed hex_to_sha256, aborting the whole run with a 500. Clear it during normalisation.
This commit is contained in:
@@ -18,6 +18,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
`["full_page", "patch"]`, and normalisation folds `full`, `full-page` and
|
||||
`Full Page` into `full_page` (with a warning) for providers without
|
||||
constrained decoding. Unknown modes still fail validation.
|
||||
- Auto-improve no longer aborts a whole run when the model fills in
|
||||
`expected_base_body_sha256`. The field is computed by the server when a
|
||||
patch is materialized, but the schema shows it to the model, and a
|
||||
non-hash value on a full-page proposal reached staging and failed
|
||||
`hex_to_sha256` with `invalid expected_base_body_sha256: expected 64 hex
|
||||
chars` (HTTP 500 from `/admin/auto-improve`), discarding every other
|
||||
proposal in the run. Normalisation now drops any model-supplied value; the
|
||||
patch path still sets it from the materialized target.
|
||||
|
||||
## [2.4.2] - 2026-09-29
|
||||
|
||||
|
||||
@@ -1511,6 +1511,11 @@ pub(crate) fn validate_response(
|
||||
fn normalize_proposal(proposal: &mut AutoImproveProposal, warnings: &mut Vec<String>) {
|
||||
normalize_kind(proposal, warnings);
|
||||
|
||||
// Server-owned: set when a patch is materialized. The schema still shows
|
||||
// it to the model, and a model-supplied non-hash value on a full-page
|
||||
// proposal would reach staging and fail the whole run on `hex_to_sha256`.
|
||||
proposal.expected_base_body_sha256 = None;
|
||||
|
||||
let original_edit_mode = proposal.edit_mode.clone();
|
||||
proposal.edit_mode = normalize_edit_mode(&original_edit_mode);
|
||||
if !original_edit_mode.trim().is_empty() && proposal.edit_mode != original_edit_mode {
|
||||
@@ -3331,6 +3336,29 @@ mod tests {
|
||||
assert_eq!(rejected[0].reason, "unsupported_edit_mode");
|
||||
}
|
||||
|
||||
/// Once `"full"` stopped being rejected, the same `gpt-oss-20b` runs
|
||||
/// failed at staging with `invalid expected_base_body_sha256: expected 64
|
||||
/// hex chars`: the model filled in a field only the server can compute.
|
||||
#[test]
|
||||
fn a_model_supplied_base_sha_is_dropped_from_full_page_proposals() {
|
||||
for supplied in ["", "null", "abc123", "N/A"] {
|
||||
let mut candidate = proposal("gotchas/thing.md", "gotcha", 0.91);
|
||||
candidate.expected_base_body_sha256 = Some(supplied.into());
|
||||
let raw = AutoImproveLlmResponse {
|
||||
summary: "ok".into(),
|
||||
proposals: vec![candidate],
|
||||
rejected_candidates: Vec::new(),
|
||||
};
|
||||
let (accepted, rejected, _) =
|
||||
validate_response(raw, &cfg(), &ExistingPageIndex::default());
|
||||
assert!(rejected.is_empty(), "{supplied:?}: got {rejected:?}");
|
||||
assert_eq!(
|
||||
accepted[0].expected_base_body_sha256, None,
|
||||
"{supplied:?} must not survive to staging"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch_to_missing_or_non_context_target_rejects() {
|
||||
let raw = AutoImproveLlmResponse {
|
||||
|
||||
Reference in New Issue
Block a user