Skip to content

feat: add mode override parameter to mental model refresh endpoint #1706

@yugandhar-maram

Description

@yugandhar-maram

Use Case

Periodic compaction of delta-mode reflections. Over time, incremental delta refreshes accumulate drift — small inaccuracies compound as each refresh only sees new facts since the last. A scheduled full re-synthesis every 48h produces a clean baseline from all facts.

Without this parameter, the only workaround is toggling a trailing space on source_query before each refresh to force a last_refreshed_source_query mismatch — a fragile hack that mutates model state for a side effect.

Problem Statement

There's no way to force a full (non-delta) refresh on a delta-mode mental model via the API. The only workaround is mutating source_query to create a mismatch with last_refreshed_source_query, which forces use_delta = False on the next refresh.

This is needed for periodic compaction — delta refreshes accumulate drift over time, and callers want to trigger a clean full re-synthesis without permanently changing the model's trigger configuration.

How This Feature Would Help

  • Clean API contract: callers express intent ("refresh fully") rather than exploiting internal side effects (source_query mismatch)
  • No state mutation: the model's config stays unchanged — the override is per-request, not persisted
  • Composable with existing triggers: delta mode stays the default for consolidation-triggered refreshes; full mode is opt-in when the caller knows it's needed

Proposed Solution

Add an optional mode query parameter to POST /v1/default/banks/{bank_id}/mental-models/{mental_model_id}/refresh:

POST /v1/default/banks/my-bank/mental-models/abc123/refresh?mode=full

  • Accepts "full" or "delta"
  • Overrides stored trigger.mode for that single refresh only
  • No parameter = current behavior (use stored trigger mode)

Suggested Implementation

Thread the override through the async task pipeline:

# api/http.py — add query param
async def api_refresh_mental_model(
    bank_id: str,
    mental_model_id: str,
    mode: str | None = Query(None, description="Override refresh mode for this refresh"),
    ...
):
    result = await app.state.memory.submit_async_refresh_mental_model(
        bank_id=bank_id,
        mental_model_id=mental_model_id,
        mode_override=mode,
        request_context=request_context,
    )

# engine/memory_engine.py — submit stores it in task payload
async def submit_async_refresh_mental_model(self, ..., mode_override: str | None = None, ...):
    task_dict = {
        "bank_id": bank_id,
        "mental_model_id": mental_model_id,
        "mode_override": mode_override,
        ...
    }

# engine/memory_engine.py — handler passes it through
async def _handle_refresh_mental_model(self, task_dict):
    }

# engine/memory_engine.py — handler passes it through
async def _handle_refresh_mental_model(self, task_dict):
    refreshed = await self.refresh_mental_model(
        bank_id=bank_id,
        mental_model_id=mental_model_id,
        mode_override=task_dict.get("mode_override"),
        request_context=internal_context,
    )

# engine/memory_engine.py:7586 — one-line change
refresh_mode = mode_override or trigger_data.get("mode") or "full"

Alternatives Considered

  1. Toggle source_query trailing space — current workaround. Mutates stored state for a side effect. Leaves invisible whitespace in the
    model. Requires the caller to understand Hindsight's internal delta-bypass logic.

  2. Patch trigger.mode to "full", refresh, patch back to "delta" — three API calls, race window where the model is misconfigured, and a
    consolidation-triggered refresh during that window would also be full (unintended).

  3. Delete + recreate the mental model — loses version history, resets created_at, triggers a fresh async initial refresh instead of a
    synchronous controlled one.

All three work but leak implementation details to the caller. A first-class mode parameter is the minimal, non-breaking addition that makes
them unnecessary.

Priority

Important - affects my workflow

Additional Context

No response

Checklist

  • I would be willing to contribute this feature

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions