Skip to content
3 changes: 2 additions & 1 deletion backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1887,7 +1887,8 @@ async def api_get_context_size(cid: str):
post_text = macros.resolve_message(
"" if settings.get("prevent_prompt_overrides") else (conv.get("post_history_instructions", "") or "")
)
user_persona_text = f"## User: {macros.user}\n{macros.resolve_message(user_desc)}" if user_desc else ""
resolved_user_desc = macros.resolve_message(user_desc)
user_persona_text = f"## User: {macros.user}\n{resolved_user_desc}" if resolved_user_desc.strip() else ""
msg_chars = sum(len(m.get("content", "") or "") for m in messages)

# Director injection
Expand Down
2 changes: 1 addition & 1 deletion backend/prompt_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def build_prefix(
parts.append(f"\n\n## Example Dialogue\n{mes}")
if resolved["post_history"]:
parts.append(f"\n\n## Additional Instructions\n{resolved['post_history']}")
if resolved["user_desc"]:
if resolved["user_desc"].strip():
user_label = macros.user if macros else "User"
parts.append(f"\n\n## User: {user_label}\n{resolved['user_desc']}")

Expand Down
11 changes: 8 additions & 3 deletions frontend/settings_personas.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ export function updateUserBtn() {
if (persona) displayName = persona.name;
}
const { conv, card } = activeLockContext();
const glyph = conv?.persona_lock_id ? CONV_LOCK_ICON : card?.persona_lock_id ? CHAR_LOCK_ICON : PERSONA_ICON;
const glyph =
conv?.persona_lock_id && card?.persona_lock_id
? CHAR_LOCK_ICON
: conv?.persona_lock_id
? CONV_LOCK_ICON
: card?.persona_lock_id
? CHAR_LOCK_ICON
: PERSONA_ICON;
const label = glyph + " " + displayName;
$("user-profile-btn").textContent = label;
const mobileBtn = $("mobile-user-profile-btn");
Expand All @@ -57,7 +64,6 @@ export function showUserModal() {
// global default (Default badge) only seeds new, unpinned chats. Nothing is
// gated: selecting another persona while pinned simply re-pins the chat.
const pinned = !!(conv?.persona_lock_id || card?.persona_lock_id);
const effectiveId = effectivePersonaId();
const personaItems = S.personas
.map((p) => {
const isActive = p.id === S.activePersonaId;
Expand All @@ -84,7 +90,6 @@ export function showUserModal() {
<div style="display:flex;align-items:center;gap:6px">
<span class="persona-name">${esc(p.name)}</span>
${isActive ? '<span class="persona-active-badge">Default</span>' : ""}
${pinned && p.id === effectiveId ? '<span class="persona-active-badge">This chat</span>' : ""}
</div>
<span class="persona-desc">${esc(p.description || "")}</span>
</div>
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/test_prompt_builder_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ def test_build_prefix_extras_appear_after_existing_body():
assert out_with == out_no_extras + "\n\nTAIL"


# -- user persona section gating ------------------------------------------


def test_build_prefix_user_section_present_with_description():
body = _system_body(build_prefix(user_description="A curious traveler.", **_BASE_KWARGS))
assert "## User:" in body
assert "A curious traveler." in body


def test_build_prefix_omits_user_section_when_description_empty():
body = _system_body(build_prefix(user_description="", **_BASE_KWARGS))
assert "## User:" not in body


def test_build_prefix_omits_user_section_when_description_whitespace_only():
body = _system_body(build_prefix(user_description=" \n\t ", **_BASE_KWARGS))
assert "## User:" not in body


# -- format_message_with_attachments source branching ---------------------


Expand Down
Loading