2026-05-25 19:02:20 +02:00

127 lines
6.4 KiB
Python

"""Centralised session-state initialisation for the Streamlit app.
All keys used throughout the app are declared here with their default values.
Calling init_state() at the top of app.py ensures every key exists before any
page tries to read it, preventing KeyError on the first load.
"""
import streamlit as st
from backend.managers.chat_manager import ChatManager
def init_state():
"""Initialise all Streamlit session-state keys with safe defaults.
Uses ``if key not in st.session_state`` guards throughout so that existing
values are never overwritten on subsequent reruns — only missing keys are
set. This means it is safe to call multiple times per session.
"""
# ── Sidebar state ─────────────────────────────────────────────────────────
# last_selected tracks the previously clicked tree node to detect new clicks
# and avoid re-running the same file-open logic on every Streamlit rerender.
if "last_selected" not in st.session_state:
st.session_state.last_selected = None
# Absolute path and workspace-relative path of the currently highlighted folder.
# Both are set together; both are cleared together when a folder is deselected.
if "selected_folder" not in st.session_state:
st.session_state.selected_folder = None
if "selected_folder_rel" not in st.session_state:
st.session_state.selected_folder_rel = None
# ── Chat manager ──────────────────────────────────────────────────────────
# ChatManager keeps the full conversation history in memory across reruns.
# Instantiated once and reused so history is not lost on page rerenders.
if "chat_manager" not in st.session_state:
st.session_state.chat_manager = ChatManager()
# ── Editor state ──────────────────────────────────────────────────────────
# Ordered list of absolute file paths currently open as editor tabs.
# The list order determines the visual tab order in the UI.
if "open_files" not in st.session_state:
# REVIEW: dead code — docstrings inside `if` blocks are plain string literals that Python
# evaluates and immediately discards; they are never visible as __doc__ and have no effect.
st.session_state.open_files = []
# Dict mapping absolute file path → current editor content (may differ from
# disk if the user has unsaved changes).
if "files_content" not in st.session_state:
# REVIEW: dead code — same issue: string literal inside `if` block is never used as a docstring.
st.session_state.files_content = {}
# Absolute path of the file whose tab is currently active in the editor.
# Must always be one of the paths in open_files, or None if no file is open.
if "active_file" not in st.session_state:
# REVIEW: dead code — string literal inside `if` block is never used as a docstring.
st.session_state.active_file = None
# REVIEW: dead code — active_tab is initialised here but never read or written anywhere else
# in the codebase; st.tabs() in editor.py does not use this key.
# Index of the active tab — kept in sync with active_file for st.tabs().
if "active_tab" not in st.session_state:
st.session_state.active_tab = 0
# REVIEW: dead code — is_editing is initialised here but never read or written anywhere else.
if "is_editing" not in st.session_state:
st.session_state.is_editing = False
# REVIEW: dead code — code_suggestions is initialised here but never read or written anywhere else.
if "code_suggestions" not in st.session_state:
st.session_state.code_suggestions = []
# Output dict from the last code execution: {stdout, stderr, return_code}.
# Initialised as empty string so the editor view can safely check falsyness.
if "code_execution_output" not in st.session_state:
st.session_state.code_execution_output = ""
# Per-file execution results: {file_path: {stdout, stderr, return_code, ast_error}}
if "exec_results" not in st.session_state:
st.session_state.exec_results = {}
# ── Chat state ────────────────────────────────────────────────────────────
# Flat list of {"role": ..., "content": ...} dicts rendered as chat bubbles.
# System messages are stored here too but skipped during display.
if "chat_history" not in st.session_state:
st.session_state.chat_history = []
# ── Agent Mode state ──────────────────────────────────────────────────────
# Boolean toggle — True while the UI is in Coding Agent mode.
if "agent_mode" not in st.session_state:
st.session_state.agent_mode = False
# The live CodingAgent instance while a task is running.
# Set by _start_agent(), cleared by _reset_agent().
if "coding_agent" not in st.session_state:
st.session_state.coding_agent = None
# Lifecycle state of the agent: "idle" | "waiting_approval" | "done".
# Controls which sub-screen render_agent_mode() displays.
if "agent_status" not in st.session_state:
st.session_state.agent_status = "idle"
# Chronological list of completed step records shown in the Agent Log expander.
# Each entry: {"thought": str, "tool": str, "arguments": dict, "result": str}
if "agent_log" not in st.session_state:
st.session_state.agent_log = []
# The action the agent has proposed but that has not yet been approved or
# rejected by the user. Stored as the raw dict returned by propose_next_action().
if "agent_pending_action" not in st.session_state:
st.session_state.agent_pending_action = None
# Web search results to be injected as context into the next AI message.
# List of {"title": str, "url": str, "snippet": str} dicts, or empty list.
if "search_results" not in st.session_state:
st.session_state.search_results = []
if __name__ == "__main__":
init_state()