SPOC_V1 / config.py
JatinAutonomousLabs's picture
Update config.py
fbec7a3 verified
raw
history blame
25.2 kB
# ============================================================================
# CENTRALIZED APPLICATION CONFIGURATION (FULL INTEGRATED)
# ============================================================================
# Drop-in config used by app_gradio.py and other modules.
# This file is resilient: prefers values from graph_config.py when available,
# but falls back to safe defaults to avoid runtime AttributeError.
# ============================================================================
import os
import json
import logging
from typing import Dict, Any, List, Optional
# Safe import of gradio (UI helpers)
try:
import gradio as gr
except Exception:
gr = None
# Initialize module logger
log = logging.getLogger("app_config")
if not log.handlers:
# Basic console logging if not configured elsewhere
h = logging.StreamHandler()
fmt = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
h.setFormatter(fmt)
log.addHandler(h)
log.setLevel(logging.INFO)
# ============================================================================
# Attempt to import graph_config for canonical values; if absent, use defaults
# ============================================================================
try:
import graph_config as graph_cfg # type: ignore
except Exception:
graph_cfg = None
def _from_graph(attr: str, default):
"""Helper to return attr from graph_cfg if available, else default."""
try:
if graph_cfg is not None and hasattr(graph_cfg, attr):
return getattr(graph_cfg, attr)
except Exception:
pass
return default
# ============================================================================
# FILES & PATHS
# ============================================================================
OUT_DIR = _from_graph("OUT_DIR", os.environ.get("OUT_DIR", "outputs"))
OUTPUTS_DIR = _from_graph("OUTPUTS_DIR", "outputs")
UPLOADS_DIR = _from_graph("UPLOADS_DIR", "uploads")
USER_ARTIFACTS_DIR = _from_graph("USER_ARTIFACTS_DIR", "outputs/user_artifacts")
EXPORTS_DIR = _from_graph("EXPORTS_DIR", os.path.join(OUT_DIR, "exports"))
FEEDBACK_STORAGE_DIR = _from_graph("FEEDBACK_STORAGE_DIR", os.path.join(OUTPUTS_DIR, "feedback"))
ARTIFACT_REGISTRY_FILE = _from_graph("ARTIFACT_REGISTRY_FILE", os.path.join(OUTPUTS_DIR, "artifact_registry.json"))
# Directory creation flag used by os.makedirs(..., exist_ok=...)
DIR_EXIST_OK = _from_graph("DIR_EXIST_OK", True)
# Ensure core directories exist early
os.makedirs(OUT_DIR, exist_ok=DIR_EXIST_OK)
os.makedirs(OUTPUTS_DIR, exist_ok=DIR_EXIST_OK)
os.makedirs(UPLOADS_DIR, exist_ok=DIR_EXIST_OK)
os.makedirs(USER_ARTIFACTS_DIR, exist_ok=DIR_EXIST_OK)
os.makedirs(os.path.dirname(ARTIFACT_REGISTRY_FILE), exist_ok=DIR_EXIST_OK)
os.makedirs(FEEDBACK_STORAGE_DIR, exist_ok=DIR_EXIST_OK)
# ============================================================================
# THEME & UI METADATA
# ============================================================================
GRADIO_THEME = _from_graph("GRADIO_THEME", "soft")
def get_theme():
"""Return a gradio theme object or None if gr is not available."""
if gr is None:
return None
theme_map = {
"default": gr.themes.Default(),
"soft": gr.themes.Soft(),
"monochrome": gr.themes.Monochrome(),
"glass": gr.themes.Glass()
}
return theme_map.get(GRADIO_THEME, gr.themes.Soft())
APP_TITLE = _from_graph("APP_TITLE", "Autonomous AI Lab")
APP_TITLE_EMOJI = _from_graph("APP_TITLE_EMOJI", "🤖")
APP_SUBTITLE = _from_graph("APP_SUBTITLE", "*AI-powered research and development assistant*")
CUSTOM_CSS = _from_graph("CUSTOM_CSS", """
/* Basic layout helpers */
.status-bar { padding: 8px 12px; background: #f8f9fa; border-radius: 6px; font-size: 0.95em; margin: 8px 0; }
.artifact-list { font-size: 0.9em; max-height: 220px; overflow-y: auto; }
.context-indicator { font-size: 0.9em; color: #444; margin-top: 6px; }
.warning-text { font-size: 0.85em; color: #666; font-style: italic; }
@media (max-width: 768px) {
.gradio-container { padding: 8px !important; }
.chatbot { height: 60vh !important; }
}
""")
# ============================================================================
# CONTEXT INDICATORS
# ============================================================================
SHOW_CONTEXT_INDICATOR = _from_graph("SHOW_CONTEXT_INDICATOR", True)
CONTEXT_INDICATOR_NEW = _from_graph("CONTEXT_INDICATOR_NEW", "📊 **New conversation**")
CONTEXT_INDICATOR_FOLLOW_UP = _from_graph("CONTEXT_INDICATOR_FOLLOW_UP", "🔄 Follow-up detected")
CONTEXT_INDICATOR_FORMAT = _from_graph("CONTEXT_INDICATOR_FORMAT", "💬 **Context: {count} exchange(s)**")
# ============================================================================
# DEFAULT CONFIG & HELPERS (used by app_gradio.get_default_state)
# ============================================================================
DEFAULT_REWORK_CYCLES = _from_graph("DEFAULT_REWORK_CYCLES", 0)
DEFAULT_APPROVED = _from_graph("DEFAULT_APPROVED", False)
DEFAULT_BUDGET_EXCEEDED = _from_graph("DEFAULT_BUDGET_EXCEEDED", False)
DEFAULT_USER_CONFIRMED_APPROVE_WITH_WARNING = _from_graph("DEFAULT_USER_CONFIRMED_APPROVE_WITH_WARNING", False)
DEFAULT_AWAITING_USER_CONFIRMATION = _from_graph("DEFAULT_AWAITING_USER_CONFIRMATION", False)
DEFAULT_CONFIG = _from_graph("DEFAULT_CONFIG", {
# Execution control & loops
"max_loops": _from_graph("DEFAULT_MAX_LOOPS", 3),
"rework_cycles": DEFAULT_REWORK_CYCLES,
# Budget & cost
"budget": _from_graph("DEFAULT_INITIAL_BUDGET", 0.10),
"current_cost": _from_graph("DEFAULT_INITIAL_COST", 0.0),
"stop_threshold": _from_graph("DEFAULT_STOP_THRESHOLD", 1.2),
"budget_buffer_percentage": _from_graph("BUDGET_BUFFER_PERCENTAGE", 0.20),
# Governance
"flexible_budget_mode": _from_graph("DEFAULT_FLEXIBLE_BUDGET_MODE", True),
"auto_accept_approved_with_warning": _from_graph("DEFAULT_AUTO_ACCEPT_APPROVED_WITH_WARNING", True),
"allow_escalation": _from_graph("DEFAULT_ALLOW_ESCALATION", False),
"preferred_tier": _from_graph("DEFAULT_PREFERRED_TIER", "standard"),
# Booleans
"approved": DEFAULT_APPROVED,
"budget_exceeded": DEFAULT_BUDGET_EXCEEDED,
"user_confirmed_approve_with_warning": DEFAULT_USER_CONFIRMED_APPROVE_WITH_WARNING,
"awaiting_user_confirmation": DEFAULT_AWAITING_USER_CONFIRMATION,
})
def get_default_config() -> Dict[str, Any]:
"""Return a shallow copy of DEFAULT_CONFIG so callers can mutate safely."""
return DEFAULT_CONFIG.copy()
# ============================================================================
# GRADIO UI CONSTANTS (used by app_gradio)
# ============================================================================
# Chat layout
CHAT_COLUMN_SCALE = _from_graph("CHAT_COLUMN_SCALE", 3)
SIDEBAR_COLUMN_SCALE = _from_graph("SIDEBAR_COLUMN_SCALE", 1)
CHATBOT_HEIGHT = _from_graph("CHATBOT_HEIGHT", 520)
CHATBOT_SHOW_LABEL = _from_graph("CHATBOT_SHOW_LABEL", False)
CHAT_COLUMN_PADDING = _from_graph("CHAT_COLUMN_PADDING", 8)
# Placeholder text
ASSISTANT_THINKING_PLACEHOLDER = _from_graph("ASSISTANT_THINKING_PLACEHOLDER", "_Thinking..._")
PLACEHOLDER_MESSAGE_INPUT = _from_graph("PLACEHOLDER_MESSAGE_INPUT", "Type your request and press Send")
# Input box sizing
INPUT_TEXTBOX_SCALE = _from_graph("INPUT_TEXTBOX_SCALE", 4)
INPUT_LINES = _from_graph("INPUT_LINES", 3)
INPUT_SHOW_LABEL = _from_graph("INPUT_SHOW_LABEL", False)
# Buttons, sizes and variants
BUTTON_SEND = _from_graph("BUTTON_SEND", "Send")
BUTTON_PROCEED = _from_graph("BUTTON_PROCEED", "Proceed")
BUTTON_CANCEL = _from_graph("BUTTON_CANCEL", "Cancel")
BUTTON_NEW_CHAT = _from_graph("BUTTON_NEW_CHAT", "New Chat")
BUTTON_RESET = _from_graph("BUTTON_RESET", "Reset System")
BUTTON_REFRESH = _from_graph("BUTTON_REFRESH", "Refresh")
BUTTON_SIZE_SMALL = _from_graph("BUTTON_SIZE_SMALL", "sm")
BUTTON_SIZE_LARGE = _from_graph("BUTTON_SIZE_LARGE", None)
BUTTON_VARIANT_PRIMARY = _from_graph("BUTTON_VARIANT_PRIMARY", "primary")
BUTTON_VARIANT_SECONDARY = _from_graph("BUTTON_VARIANT_SECONDARY", "secondary")
BUTTON_VARIANT_STOP = _from_graph("BUTTON_VARIANT_STOP", "stop")
PROCEED_BUTTON_SCALE = _from_graph("PROCEED_BUTTON_SCALE", 1)
CANCEL_BUTTON_SCALE = _from_graph("CANCEL_BUTTON_SCALE", 1)
SUBMIT_BUTTON_SCALE = _from_graph("SUBMIT_BUTTON_SCALE", 1)
PROCEED_BUTTON_SCALE = _from_graph("PROCEED_BUTTON_SCALE", 1)
BUTTON_VARIANT_PRIMARY = _from_graph("BUTTON_VARIANT_PRIMARY", "primary")
# Approval box behavior
APPROVAL_BOX_DEFAULT_VISIBLE = _from_graph("APPROVAL_BOX_DEFAULT_VISIBLE", False)
# Budget UI
LABEL_MAX_BUDGET = _from_graph("LABEL_MAX_BUDGET", "Max Budget (USD)")
BUDGET_DEFAULT_VALUE = _from_graph("BUDGET_DEFAULT_VALUE", 0.10)
BUDGET_MINIMUM = _from_graph("BUDGET_MINIMUM", 0.0)
BUDGET_STEP = _from_graph("BUDGET_STEP", 0.01)
BUDGET_INPUT_SCALE = _from_graph("BUDGET_INPUT_SCALE", 1)
LABEL_FLEXIBLE_BUDGET = _from_graph("LABEL_FLEXIBLE_BUDGET", "Flexible Budget")
LABEL_PREFERRED_TIER = _from_graph("LABEL_PREFERRED_TIER", "Preferred Tier")
TIER_CHOICES = _from_graph("TIER_CHOICES", ["lite", "standard", "full"])
TIER_DEFAULT = _from_graph("TIER_DEFAULT", "standard")
# Feedback UI
SECTION_FEEDBACK = _from_graph("SECTION_FEEDBACK", "Feedback & Rating")
FEEDBACK_PURPOSE_TEXT = _from_graph("FEEDBACK_PURPOSE_TEXT", "Help us improve by rating this run.")
FEEDBACK_CATEGORIES = _from_graph("FEEDBACK_CATEGORIES", {"quality": "Quality", "usability": "Usability", "other": "Other"})
LABEL_RATING = _from_graph("LABEL_RATING", "Rating")
RATING_MIN = _from_graph("RATING_MIN", 1)
RATING_MAX = _from_graph("RATING_MAX", 5)
RATING_DEFAULT_VALUE = _from_graph("RATING_DEFAULT_VALUE", 5)
LABEL_FEEDBACK_COMMENTS = _from_graph("LABEL_FEEDBACK_COMMENTS", "Comments")
FEEDBACK_COMMENT_LINES = _from_graph("FEEDBACK_COMMENT_LINES", 3)
FEEDBACK_PLACEHOLDER = _from_graph("FEEDBACK_PLACEHOLDER", "Optional: leave a comment about your experience")
BUTTON_SUBMIT_FEEDBACK = _from_graph("BUTTON_SUBMIT_FEEDBACK", "Submit Feedback")
FEEDBACK_THANK_YOU_MESSAGES = _from_graph("FEEDBACK_THANK_YOU_MESSAGES", [
"Thanks — your feedback helps us improve!",
"Appreciate your feedback — thank you!",
"Thanks! We logged your feedback."
])
# Files UI
SECTION_FILES_ARTIFACTS = _from_graph("SECTION_FILES_ARTIFACTS", "Files & Artifacts")
LABEL_UPLOAD_FILES = _from_graph("LABEL_UPLOAD_FILES", "Upload files")
ALLOWED_FILE_TYPES = _from_graph("ALLOWED_FILE_TYPES", None) # None means allow any
UPLOAD_STATUS_SHOW_LABEL = _from_graph("UPLOAD_STATUS_SHOW_LABEL", False)
PLACEHOLDER_NO_FILES = _from_graph("PLACEHOLDER_NO_FILES", "No files uploaded")
# Debug / JSON display
SECTION_DEBUG = _from_graph("SECTION_DEBUG", "Debug")
ACCORDION_DEBUG_OPEN = _from_graph("ACCORDION_DEBUG_OPEN", False)
LABEL_AGENT_STATE = _from_graph("LABEL_AGENT_STATE", "Agent State (debug)")
DEBUG_JSON_VISIBLE = _from_graph("DEBUG_JSON_VISIBLE", False)
# ============================================================================
# UI text / status messages (fallbacks)
# ============================================================================
RESET_SUCCESS_MESSAGE = _from_graph("RESET_SUCCESS_MESSAGE", "✅ System reset complete.")
RESET_LOG_MESSAGE = _from_graph("RESET_LOG_MESSAGE", RESET_SUCCESS_MESSAGE)
RESET_ERROR_MESSAGE_PREFIX = _from_graph("RESET_ERROR_MESSAGE_PREFIX", "⚠️ Reset error: ")
NO_FILE_UPLOADED_MESSAGE = _from_graph("NO_FILE_UPLOADED_MESSAGE", "No file uploaded.")
FILE_UPLOAD_SUCCESS_PREFIX = _from_graph("FILE_UPLOAD_SUCCESS_PREFIX", "📎 Uploaded ")
FILE_UPLOAD_SUCCESS_SUFFIX = _from_graph("FILE_UPLOAD_SUCCESS_SUFFIX", " file(s): ")
UPLOAD_LOG_MESSAGE_PREFIX = _from_graph("UPLOAD_LOG_MESSAGE_PREFIX", "Uploaded: ")
NO_ARTIFACTS_MESSAGE = _from_graph("NO_ARTIFACTS_MESSAGE", "No artifacts yet.")
NO_ARTIFACTS_GENERATED_MESSAGE = _from_graph("NO_ARTIFACTS_GENERATED_MESSAGE", "No artifacts generated yet.")
CLEAR_CONVERSATION_MESSAGE = _from_graph("CLEAR_CONVERSATION_MESSAGE", "🗑️ Conversation cleared.")
ASSISTANT_THINKING_PLACEHOLDER = _from_graph("ASSISTANT_THINKING_PLACEHOLDER", ASSISTANT_THINKING_PLACEHOLDER)
# Execution status messages
STATUS_ANALYZING = _from_graph("STATUS_ANALYZING", "🔄 Analyzing request...")
STATUS_READY = _from_graph("STATUS_READY", "💬 Ready")
STATUS_AWAITING_APPROVAL = _from_graph("STATUS_AWAITING_APPROVAL", "⏸️ Awaiting approval")
STATUS_TASK_APPROVED = _from_graph("STATUS_TASK_APPROVED", "✅ Task approved. Starting...")
STATUS_RUNNING_PREFIX = _from_graph("STATUS_RUNNING_PREFIX", "🔄 ")
STATUS_RUNNING_SUFFIX = _from_graph("STATUS_RUNNING_SUFFIX", " | Cost: ${cost:.2f} / ${threshold:.2f} (120%)")
STATUS_TASK_COMPLETE = _from_graph("STATUS_TASK_COMPLETE", "✅ Task complete | Cost: ${cost:.2f} (${budget} + {buffer}% buffer)")
STATUS_BUDGET_EXCEEDED = _from_graph("STATUS_BUDGET_EXCEEDED", "⚠️ Budget limit reached | Used: ${cost:.2f} / ${threshold:.2f}")
STATUS_PARTIAL_COMPLETION = _from_graph("STATUS_PARTIAL_COMPLETION", "⚠️ Partial completion | Cost: ${cost:.2f}")
STATUS_CANCELLED = _from_graph("STATUS_CANCELLED", "⏸️ Cancelled. Ready.")
STATUS_NO_RUNTIME = _from_graph("STATUS_NO_RUNTIME", "❌ No runtime")
STATUS_ERROR_PREFIX = _from_graph("STATUS_ERROR_PREFIX", "❌ Error: ")
ERROR_EXECUTION_PREFIX = _from_graph("ERROR_EXECUTION_PREFIX", "❌ Execution failed: ")
# ----------------------------------------------------------------------------
# AVATAR CONFIGURATION — minimal, robust, and guaranteed to return >= 2 items
# ----------------------------------------------------------------------------
import os
from typing import List
# Prefer a local avatar placed at ./assets/avatar.png (relative to project root)
LOCAL_AVATAR_PATH = os.path.join(os.path.dirname(__file__), "assets", "avatar.png")
# Secondary local candidate(s)
LOCAL_AVATAR_CANDIDATES = [
LOCAL_AVATAR_PATH,
os.path.join(os.path.dirname(__file__), "static", "avatar.png"),
os.path.join(os.path.dirname(__file__), "assets", "bot.png"),
]
# Stable remote fallbacks (used only if no local file is present)
REMOTE_FALLBACKS = [
"https://i.imgur.com/b5OqI32.png",
"https://i.imgur.com/8Km9tLL.png"
]
def get_avatar_images() -> List[str]:
"""
Return a list of avatar image paths/URLs with length >= 2.
Priority:
1) Use two local images if available
2) If only one local image found, duplicate it (safe)
3) Else return two stable remote fallback URLs
"""
imgs: List[str] = []
# 1) collect existing local candidates
for candidate in LOCAL_AVATAR_CANDIDATES:
try:
if candidate and os.path.exists(candidate):
imgs.append(candidate)
except Exception:
# ignore filesystem oddities; continue to next candidate
continue
if len(imgs) >= 2:
break
# 2) if only one local image found, duplicate it so Gradio has two entries
if len(imgs) == 1:
imgs.append(imgs[0])
# 3) if none found, use remote fallbacks
if not imgs:
imgs = REMOTE_FALLBACKS.copy()
# 4) final safety: ensure length >= 2 (pad with last element)
while len(imgs) < 2:
imgs.append(imgs[-1])
return imgs
# ============================================================================
# Server config helpers (used at app launch)
# ============================================================================
DEFAULT_SERVER_CONFIG = _from_graph("DEFAULT_SERVER_CONFIG", {
"server_name": "0.0.0.0",
"server_port": int(os.environ.get("PORT", os.environ.get("SERVER_PORT", 7860))),
"share": False, # gradio share link
"debug": False
})
def get_server_config() -> Dict[str, Any]:
"""Return server config for demo.launch(**cfg)."""
cfg = DEFAULT_SERVER_CONFIG.copy()
# Allow environment overrides
try:
if os.environ.get("GRADIO_SHARE", "").lower() in ("1", "true", "yes"):
cfg["share"] = True
except Exception:
pass
return cfg
def log_launch_banner(port: int):
"""Small friendly banner used at startup logs."""
try:
log.info("=" * 60)
log.info(f"Launching Gradio app on port {port}{APP_TITLE} {APP_TITLE_EMOJI}")
log.info("=" * 60)
except Exception:
pass
# ============================================================================
# Small validation helper used by app_gradio feedback flow
# ============================================================================
def validate_rating(rating: int):
if not isinstance(rating, int):
raise ValueError("Rating must be integer")
if rating < RATING_MIN or rating > RATING_MAX:
raise ValueError(f"Rating must be between {RATING_MIN} and {RATING_MAX}")
return True
# ----------------------------------------------------------------------------
# INITIAL STATUS MESSAGE (used by app_gradio)
# ----------------------------------------------------------------------------
INITIAL_STATUS = "💬 Ready to help"
# ----------------------------------------------------------------------------
# DEFAULT CHECKBOX STATES & GOVERNANCE FLAGS (used in app_gradio sidebar)
# ----------------------------------------------------------------------------
# Liberal defaults (auto-approve & flexible budgets enabled by default)
DEFAULT_FLEXIBLE_CHECKBOX = True
DEFAULT_AUTO_ACCEPT_CHECKBOX = True
# Escalation is optional, off by default
DEFAULT_ALLOW_ESCALATION = False
# ----------------------------------------------------------------------------
# ADD MISSING UI LABELS (small, safe defaults)
# ----------------------------------------------------------------------------
# Label for the "auto accept" checkbox in the UI sidebar
LABEL_AUTO_ACCEPT = "Auto-accept 'approve with warning' decisions"
# (Ensure there's also a corresponding checkbox default; safe default = True)
DEFAULT_AUTO_ACCEPT_CHECKBOX = globals().get("DEFAULT_AUTO_ACCEPT_CHECKBOX", True)
# ----------------------------------------------------------------------------
# FILES / ARTIFACTS UI LABELS
# ----------------------------------------------------------------------------
# Label shown on the "download artifacts" Files component
LABEL_DOWNLOAD_ARTIFACTS = "Download artifacts"
# Helpful siblings used elsewhere in the UI (safe defaults)
LABEL_UPLOAD_FILES = globals().get("LABEL_UPLOAD_FILES", "Upload files")
LABEL_DOWNLOAD_ZIP = globals().get("LABEL_DOWNLOAD_ZIP", "Download all as .zip")
PLACEHOLDER_NO_FILES = globals().get("PLACEHOLDER_NO_FILES", "No files available")
# ----------------------------------------------------------------------------
# PLAN DISPLAY / ESTIMATION TEXT CONSTANTS (used by start_estimation)
# ----------------------------------------------------------------------------
# Section headers
PLAN_HEADER = "**📋 Execution Plan:**\n"
PLAN_ESTIMATE_HEADER = "\n\n**📊 Estimate:**\n"
# Step prefix used in list output
PLAN_STEP_PREFIX = "- "
# Formatting patterns for estimation details
PLAN_ATTEMPTS_FORMAT = "- Attempts: up to **{}**\n"
PLAN_COST_FORMAT = "- Estimated cost: **${}**\n"
PLAN_BUFFER_FORMAT = "- With 20% buffer: **${:.2f}**"
# Default initial max loops used by estimation when no override provided
DEFAULT_MAX_LOOPS_INITIAL = globals().get("DEFAULT_MAX_LOOPS_INITIAL", 0)
# Budget multipliers (used when showing cost with buffer)
BUDGET_BUFFER_MULTIPLIER = globals().get("BUDGET_BUFFER_MULTIPLIER", 1.20)
# ----------------------------------------------------------------------------
# Additional safe defaults to prevent missing-attribute errors (add these)
# ----------------------------------------------------------------------------
# Feedback / logging helpers
LOG_FEEDBACK_SUBMISSION_FAILED = "Failed saving feedback"
FEEDBACK_THANK_YOU_MESSAGES = globals().get("FEEDBACK_THANK_YOU_MESSAGES", [
"Thanks — your feedback helps us improve!",
"Appreciate your feedback — thank you!",
"Thanks! We logged your feedback."
])
# Feedback defaults / counts
DEFAULT_COMMENT = "No comment provided"
DEFAULT_RUN_COST = 0.0
DEFAULT_AVG_REWARD = 0.0
DEFAULT_FEEDBACK_COUNT = 0
# Optional feedback agent hooks (module.function style) - leave None if unused
FEEDBACK_AGENT_MODULE = None
FEEDBACK_AGENT_FUNCTION = None
# Download / artifacts label (if not already present)
LABEL_DOWNLOAD_ARTIFACTS = globals().get("LABEL_DOWNLOAD_ARTIFACTS", "Download artifacts")
# Default budget value shown in the UI (USD)
DEFAULT_BUDGET_DISPLAY = globals().get("DEFAULT_BUDGET_DISPLAY", 0.10)
# ============================================================================
# Export list for other modules
# ============================================================================
__all__ = [
# Paths
"OUT_DIR", "OUTPUTS_DIR", "UPLOADS_DIR", "USER_ARTIFACTS_DIR", "EXPORTS_DIR",
"ARTIFACT_REGISTRY_FILE", "FEEDBACK_STORAGE_DIR", "DIR_EXIST_OK",
# Theme & UI
"get_theme", "APP_TITLE", "APP_TITLE_EMOJI", "APP_SUBTITLE", "CUSTOM_CSS",
"GRADIO_THEME",
# Context
"SHOW_CONTEXT_INDICATOR", "CONTEXT_INDICATOR_NEW", "CONTEXT_INDICATOR_FOLLOW_UP",
"CONTEXT_INDICATOR_FORMAT",
# Default config
"DEFAULT_CONFIG", "get_default_config",
# UI constants
"CHAT_COLUMN_SCALE", "SIDEBAR_COLUMN_SCALE", "CHATBOT_HEIGHT", "CHATBOT_SHOW_LABEL",
"PLACEHOLDER_MESSAGE_INPUT", "INPUT_TEXTBOX_SCALE", "INPUT_LINES", "INPUT_SHOW_LABEL",
"BUTTON_SEND", "BUTTON_PROCEED", "BUTTON_CANCEL", "BUTTON_NEW_CHAT", "BUTTON_RESET",
"BUTTON_REFRESH", "BUTTON_SUBMIT_FEEDBACK", "BUTTON_VARIANT_PRIMARY",
"BUTTON_VARIANT_SECONDARY", "BUTTON_VARIANT_STOP", "BUTTON_SIZE_SMALL",
"BUTTON_SIZE_LARGE", "PROCEED_BUTTON_SCALE", "CANCEL_BUTTON_SCALE", "SUBMIT_BUTTON_SCALE",
"APPROVAL_BOX_DEFAULT_VISIBLE",
# Budget & tiers
"LABEL_MAX_BUDGET", "BUDGET_DEFAULT_VALUE", "BUDGET_MINIMUM", "BUDGET_STEP", "BUDGET_INPUT_SCALE",
"LABEL_FLEXIBLE_BUDGET", "LABEL_PREFERRED_TIER", "TIER_CHOICES", "TIER_DEFAULT",
# Feedback & files
"SECTION_FEEDBACK", "FEEDBACK_PURPOSE_TEXT", "FEEDBACK_CATEGORIES", "LABEL_RATING",
"RATING_MIN", "RATING_MAX", "RATING_DEFAULT_VALUE", "LABEL_FEEDBACK_COMMENTS", "FEEDBACK_PLACEHOLDER",
"SECTION_FILES_ARTIFACTS", "LABEL_UPLOAD_FILES", "ALLOWED_FILE_TYPES", "UPLOAD_STATUS_SHOW_LABEL",
"PLACEHOLDER_NO_FILES", "SECTION_DEBUG", "ACCORDION_DEBUG_OPEN", "LABEL_AGENT_STATE", "DEBUG_JSON_VISIBLE",
"FEEDBACK_THANK_YOU_MESSAGES",
# Messages & status
"RESET_SUCCESS_MESSAGE", "RESET_LOG_MESSAGE", "RESET_ERROR_MESSAGE_PREFIX", "NO_FILE_UPLOADED_MESSAGE",
"FILE_UPLOAD_SUCCESS_PREFIX", "FILE_UPLOAD_SUCCESS_SUFFIX", "NO_ARTIFACTS_MESSAGE", "NO_ARTIFACTS_GENERATED_MESSAGE",
"CLEAR_CONVERSATION_MESSAGE", "ASSISTANT_THINKING_PLACEHOLDER",
"STATUS_ANALYZING", "STATUS_READY", "STATUS_AWAITING_APPROVAL", "STATUS_TASK_APPROVED",
"STATUS_TASK_COMPLETE", "STATUS_PARTIAL_COMPLETION", "STATUS_CANCELLED", "STATUS_ERROR_PREFIX",
"ERROR_EXECUTION_PREFIX",
# Helpers
"get_avatar_images", "get_server_config", "log_launch_banner", "validate_rating", "INITIAL_STATUS",
"DEFAULT_FLEXIBLE_CHECKBOX",
"DEFAULT_AUTO_ACCEPT_CHECKBOX",
"DEFAULT_ALLOW_ESCALATION",
"LABEL_AUTO_ACCEPT",
"DEFAULT_AUTO_ACCEPT_CHECKBOX",
# UI / theme / metadata & helpers
'APP_TITLE',
'APP_TITLE_EMOJI',
'APP_SUBTITLE',
'CUSTOM_CSS',
'GRADIO_THEME',
'get_theme',
# Context indicators & initial status
'SHOW_CONTEXT_INDICATOR',
'CONTEXT_INDICATOR_NEW',
'CONTEXT_INDICATOR_FOLLOW_UP',
'CONTEXT_INDICATOR_FORMAT',
'INITIAL_STATUS',
# Paths & artifact flags
'USER_ARTIFACTS_DIR',
'DIR_EXIST_OK',
# Avatar helpers
'get_avatar_images',
'AVATAR_USER',
'AVATAR_BOT',
# Default config & helpers
'DEFAULT_CONFIG',
'get_default_config',
# Checkbox defaults / governance & labels
'DEFAULT_FLEXIBLE_CHECKBOX',
'DEFAULT_AUTO_ACCEPT_CHECKBOX',
'DEFAULT_ALLOW_ESCALATION',
'LABEL_AUTO_ACCEPT',
# Button & UI labels commonly used
'BUTTON_SEND',
'BUTTON_PROCEED',
'BUTTON_CANCEL',
'BUTTON_NEW_CHAT',
'BUTTON_RESET',
'BUTTON_REFRESH',
'BUTTON_SUBMIT_FEEDBACK',
# Placeholders / small text
'ASSISTANT_THINKING_PLACEHOLDER',
'PLACEHOLDER_MESSAGE_INPUT',
'PLACEHOLDER_NO_FILES',
# Server / logging helpers
'get_server_config',
'log_launch_banner',
# Validation
'validate_rating',
#artifacts
'LABEL_DOWNLOAD_ARTIFACTS',
'LABEL_UPLOAD_FILES',
'LABEL_DOWNLOAD_ZIP',
'PLACEHOLDER_NO_FILES',
#plan
'PLAN_HEADER',
'PLAN_ESTIMATE_HEADER',
'PLAN_STEP_PREFIX',
'PLAN_ATTEMPTS_FORMAT',
'PLAN_COST_FORMAT',
'PLAN_BUFFER_FORMAT',
'DEFAULT_MAX_LOOPS_INITIAL',
'BUDGET_BUFFER_MULTIPLIER',
'LOG_FEEDBACK_SUBMISSION_FAILED',
'DEFAULT_COMMENT',
'DEFAULT_RUN_COST',
'DEFAULT_AVG_REWARD',
'DEFAULT_FEEDBACK_COUNT',
'FEEDBACK_AGENT_MODULE',
'FEEDBACK_AGENT_FUNCTION',
'LABEL_DOWNLOAD_ARTIFACTS',
'DEFAULT_BUDGET_DISPLAY'
]