Skip to content

fix(core): sync system prompt hash when overriding via runtime API#1702

Closed
mvanhorn wants to merge 2 commits into
Hmbown:mainfrom
mvanhorn:fix/1688-system-prompt-override-hash-sync
Closed

fix(core): sync system prompt hash when overriding via runtime API#1702
mvanhorn wants to merge 2 commits into
Hmbown:mainfrom
mvanhorn:fix/1688-system-prompt-override-hash-sync

Conversation

@mvanhorn

Copy link
Copy Markdown
Contributor

Summary

POST /v1/threads accepts a system_prompt field and stores it on the session, but the next refresh_system_prompt call rebuilt the stable baseline and overwrote it because session.last_system_prompt_hash was still the pre-override value. The model request fired with the rebuilt baseline, not the override.

Add sync_session_system_prompt_hash that recomputes last_system_prompt_hash after a thread API override, and call it where the override is installed. Now refresh_system_prompt's hash-equality short-circuit fires and the override survives.

Factor the stable-prompt construction into stable_system_prompt_for_mode so the sync and the refresh paths share it.

Why this matters

Reporter quote from #1688: "system_prompt on POST /v1/threads is accepted and stored but discarded before the model request." Root cause: hash drift between session.last_system_prompt_hash (pre-override baseline) and the new override. The new tests (text_system_prompt_override_via_sync_session_survives_refresh and the Blocks variant) lock the regression in.

Closes #1688

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a mechanism to synchronize the system prompt hash when an override is present, ensuring that manual overrides are not immediately overwritten by the refresh logic. It refactors system prompt generation into a standalone method and adds unit tests to verify that overrides survive session refreshes. Review feedback suggests simplifying the hash synchronization logic using Option::map and highlights a potential limitation caused by hardcoding AppMode::Agent, which could lead to unexpected refreshes in other modes.

Comment thread crates/tui/src/core/engine.rs Outdated
Comment on lines +1800 to +1805
self.session.last_system_prompt_hash = if self.session.system_prompt.is_some() {
let stable_prompt = self.stable_system_prompt_for_mode(AppMode::Agent);
Some(system_prompt_hash(stable_prompt.as_ref()))
} else {
Some(system_prompt_hash(None))
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This logic can be simplified using Option::map. Additionally, using None for the last_system_prompt_hash when no override is present is more idiomatic and achieves the same result of forcing a refresh on the next turn (since None != Some(stable_hash) will be true).

Note that hardcoding AppMode::Agent means overrides will be lost if the session is used in Plan or Yolo mode, as refresh_system_prompt will detect a hash mismatch for those modes. Given the current architecture, this is a reasonable default for API-driven syncs, but it remains a limitation for TUI users who might be in a different mode when a sync occurs.

        self.session.last_system_prompt_hash = self.session.system_prompt.as_ref().map(|_| {
            let stable_prompt = self.stable_system_prompt_for_mode(AppMode::Agent);
            system_prompt_hash(stable_prompt.as_ref())
        });

…t_hash with Option::map

Use Option::map to avoid the if-else branch and let the no-override case
fall through as None (which compares unequal to any Some(stable_hash) on
the next refresh, so the override-clearing semantics are preserved without
hashing None explicitly).
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Addressed in e30e09a -- used Option::map so the no-override path lands None instead of Some(hash_of_None). Behavior is the same: a None hash compares unequal to any Some(stable_hash) on the next refresh, so override-clearing still works without explicitly hashing None.

Kept AppMode::Agent hardcoded for now -- the runtime API (POST /v1/threads) is agent-mode-scoped and refresh_system_prompt is invoked with the same mode the request was processed in, so the hash will still match. Happy to thread the mode through if you'd rather make this defensive against future mode-mixed sessions.

Tests: cargo test -p deepseek-tui --bin deepseek-tui sync_session -- both regression tests pass.

@Hmbown Hmbown added this to the v0.8.39 milestone May 16, 2026
@Hmbown

Hmbown commented May 17, 2026

Copy link
Copy Markdown
Owner

Thanks for the focused fix and tests. The runtime system_prompt override regression was harvested into v0.8.39 via #1734 and credited in the changelog, so I am closing this PR to keep the queue clean. If the latest release still drops thread-level system_prompt overrides, please reopen with the failing request payload.

@Hmbown Hmbown closed this May 17, 2026
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.

Runtime API: system_prompt on POST /v1/threads is accepted and stored but discarded before the model request

2 participants