import gradio as gr import logging import uuid from datetime import datetime import pandas as pd from i18n import get_text from model_handler import ModelHandler from tab_chat import create_chat_tab from tab_chat import update_language as update_chat_language from tab_code import create_code_tab from tab_code import update_language as update_code_language from tab_smart_writer import create_smart_writer_tab from tab_smart_writer import update_language as update_writer_language from tab_welcome import create_welcome_tab from tab_welcome import update_language as update_welcome_language # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) def get_history_df_from_app(history): if not history: return pd.DataFrame( {"ID": pd.Series(dtype="str"), "对话": pd.Series(dtype="str")} ) df = pd.DataFrame(history) if "id" in df.columns and "title" in df.columns: return df[["id", "title"]].rename(columns={"id": "ID", "对话": "对话"}) return pd.DataFrame({"ID": pd.Series(dtype="str"), "对话": pd.Series(dtype="str")}) def on_app_load(request: gr.Request, history, conv_id, current_lang_state): """ Handles the application's initial state on load. - Determines language from URL parameter. - Loads conversation history or creates a new one. """ # --- Language Detection --- query_params = dict(request.query_params) url_lang = query_params.get("lang") updated_lang = current_lang_state # Start with the default if url_lang and url_lang in ["en", "zh"]: updated_lang = url_lang # --- History Loading Logic --- if not history: # First time ever, create a new conversation conv_id = str(uuid.uuid4()) new_convo_title = get_text("chat_new_conversation_title", updated_lang) new_convo = { "id": conv_id, "title": new_convo_title, "messages": [], "timestamp": datetime.now().isoformat(), } history = [new_convo] return ( conv_id, history, gr.update(value=get_history_df_from_app(history)), [], updated_lang, ) if conv_id and any(c["id"] == conv_id for c in history): # Valid last session, load it for convo in history: if convo["id"] == conv_id: return ( conv_id, history, gr.update(value=get_history_df_from_app(history)), convo["messages"], updated_lang, ) # Fallback to most recent conversation most_recent_convo = history[0] return ( most_recent_convo["id"], history, gr.update(value=get_history_df_from_app(history)), most_recent_convo["messages"], updated_lang, ) CSS = """ #chatbot { height: calc(100vh - 21px - 16px); max-height: 1500px; } footer { display: none !important; } /* Disable transition and animation for no-transition class */ .no-transition, .no-transition * { transition: none !important; animation: none !important; animation-play-state: paused !important; } """ if __name__ == "__main__": # Instantiate the model handler with the configuration logger.info("Starting Ling Space Application...") model_handler = ModelHandler() with gr.Blocks(analytics_enabled=False, fill_height=True, fill_width=True) as demo: # Language State current_lang_state = gr.State("en") # --- Collect all components that need language updates --- all_i18n_outputs = [] with gr.Tabs(elem_id="indicator-space-app") as tabs: welcome_components = create_welcome_tab(current_lang_state.value) # The order of components MUST be consistent welcome_outputs = [ welcome_components["tab"], welcome_components["header"], welcome_components["description"], welcome_components["chat_description"], welcome_components["code_description"], welcome_components["writer_description"], welcome_components["lang_select_header"], welcome_components["en_button"], welcome_components["zh_button"], ] all_i18n_outputs.extend(welcome_outputs) # --- Chat Tab --- with gr.TabItem( get_text("chat_tab_title", current_lang_state.value) ) as chat_tab: chat_components = create_chat_tab( current_lang_state.value, current_lang_state ) chat_outputs = [ chat_tab, chat_components["new_chat_btn"], chat_components["history_df"], chat_components["chatbot"], chat_components["textbox"], chat_components["submit_btn"], chat_components["recommended_title"], chat_components["recommended_dataset"], chat_components["system_prompt_textbox"], chat_components["temperature_slider"], ] all_i18n_outputs.extend(chat_outputs) chat_tab.select( fn=None, js="() => {window.dispatchEvent(new CustomEvent('tabSelect.chat')); console.log('this'); return null;}", ) # --- Code Tab --- with gr.TabItem( get_text("code_tab_title", current_lang_state.value) ) as code_tab: code_components = create_code_tab( current_lang_state.value, current_lang_state ) code_outputs = [ code_tab, code_components["prompt_input"], code_components["overall_style_input"], code_components["decoration_input"], code_components["palette_display"], code_components["generate_style_btn"], code_components["examples_title"], code_components["examples_dataset"], code_components["generate_button"], code_components["preview_tab"], code_components["preview_header"], code_components["source_code_tab"], code_components["source_code_header"], code_components["code_output"], code_components["refresh_button"], code_components["log_chatbot"], code_components["js_error_channel"], # fullscreen_button is updated in its own handler, so it's excluded here ] all_i18n_outputs.extend(code_outputs) code_tab.select( fn=None, js="() => {window.dispatchEvent(new CustomEvent('tabSelect.code')); return null;}", ) # --- Writer Tab --- with gr.TabItem( get_text("writer_tab_title", current_lang_state.value) ) as writer_tab: writer_components = create_smart_writer_tab(current_lang_state.value) writer_outputs = [ writer_tab, writer_components["style_input"], writer_components["kb_accordion"], writer_components["kb_input"], writer_components["btn_suggest_kb"], writer_components["suggested_kb_dataframe"], writer_components["short_outline_accordion"], writer_components["short_outline_input"], writer_components["btn_sync_outline"], writer_components["long_outline_accordion"], writer_components["long_outline_input"], writer_components["flow_suggestion_display"], writer_components["btn_accept_flow"], writer_components["btn_change_flow"], writer_components["inspiration_prompt_input"], writer_components["prompt_suggestions_dataset"], writer_components["refresh_suggestions_btn"], writer_components["btn_generate_para"], writer_components["btn_change_para"], writer_components["btn_accept_para"], writer_components["para_suggestion_display"], writer_components["polish_title"], writer_components["polish_soon"], writer_components["stats_display"], writer_components["editor"], ] all_i18n_outputs.extend(writer_outputs) writer_tab.select( fn=None, js="() => {window.dispatchEvent(new CustomEvent('tabSelect.writing')); return null;}", ) # --- Language Change Handler --- def on_language_change(lang): # Dispatch updates for each tab welcome_updates = update_welcome_language(lang, welcome_components) chat_updates = update_chat_language(lang, chat_components) chat_updates[chat_tab] = gr.update(label=get_text("chat_tab_title", lang)) code_updates = update_code_language(lang, code_components) code_updates[code_tab] = gr.update(label=get_text("code_tab_title", lang)) writer_updates = update_writer_language(lang, writer_components) writer_updates[writer_tab] = gr.update( label=get_text("writer_tab_title", lang) ) all_updates = { **welcome_updates, **chat_updates, **code_updates, **writer_updates, } return tuple(all_updates.get(comp) for comp in all_i18n_outputs) current_lang_state.change( fn=on_language_change, inputs=[current_lang_state], outputs=all_i18n_outputs, show_progress="hidden", ) # --- App Load Handler --- conversation_store = chat_components["conversation_store"] current_conversation_id = chat_components["current_conversation_id"] history_df = chat_components["history_df"] chatbot = chat_components["chatbot"] demo.load( on_app_load, inputs=[conversation_store, current_conversation_id, current_lang_state], outputs=[ current_conversation_id, conversation_store, history_df, chatbot, current_lang_state, ], js="() => {window.dispatchEvent(new CustomEvent('appStart')); console.log('appStart'); return {};}", ) demo.queue(default_concurrency_limit=32, max_size=128) # Launch the Gradio application demo.launch( theme=gr.themes.Default(), ssr_mode=False, max_threads=64, css=CSS, head="", head_paths=["./static/toastify.html", "./static/app.html"], )