127 lines
6.4 KiB
Python
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()
|