persona-ui / state.py
Jac-Zac
Big refactoring
b279884
from typing import Literal, NotRequired, TypedDict
import streamlit as st
from utils.helpers import session_key
PendingChatAction = Literal["new_user_prompt", "regenerate_after_edit"]
class ChatMessage(TypedDict):
role: str
content: str
_contrast: NotRequired[object]
_needs_contrast: NotRequired[bool]
class ChatState(TypedDict):
messages: list[ChatMessage]
persona_id: str | None
prompt_mode: str
def chat_session_key(model_name: str, dataset_source: str) -> str:
"""Build the session-state key for a chat conversation.
A model/backend switch changes *how* the next turn is generated, not which
conversation the user is looking at. Keeping the model out of the key means
toggling local/remote execution (or selecting another model) no longer makes
an existing thread appear to vanish behind a fresh empty state.
``model_name`` stays in the signature for call-site compatibility and to
make the intent explicit where chat state is requested.
"""
_ = model_name
return session_key("chat_state", dataset_source)
def default_chat_state() -> ChatState:
return {
"messages": [],
"persona_id": None,
"prompt_mode": "templated",
}
def reset_chat_context_state(
state: ChatState,
persona_id: str,
prompt_mode: str,
*ui_keys: str,
) -> None:
"""Reset one chat context and clear any related widget state."""
state["messages"] = []
state["persona_id"] = persona_id
state["prompt_mode"] = prompt_mode
for key in ui_keys:
st.session_state.pop(key, None)
def get_chat_state(model_name: str, dataset_source: str) -> ChatState:
"""Return the mutable chat state for the active context."""
key = chat_session_key(model_name, dataset_source)
return st.session_state.setdefault(key, default_chat_state())