Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -2569,533 +2569,282 @@ custom_css = """
|
|
2569 |
|
2570 |
# Gradio μΈν°νμ΄μ€ μμ±
|
2571 |
def create_interface():
|
2572 |
-
|
2573 |
-
|
2574 |
-
|
2575 |
-
|
2576 |
-
|
2577 |
-
|
2578 |
-
|
2579 |
-
|
2580 |
-
|
2581 |
-
|
2582 |
-
|
2583 |
-
|
2584 |
-
|
2585 |
-
|
2586 |
-
|
2587 |
-
|
2588 |
-
|
2589 |
-
|
2590 |
-
|
2591 |
-
|
2592 |
-
|
2593 |
-
|
2594 |
-
|
2595 |
-
|
2596 |
-
|
2597 |
-
|
2598 |
-
|
2599 |
-
|
2600 |
-
|
2601 |
-
|
2602 |
-
|
2603 |
-
|
2604 |
-
|
2605 |
-
|
2606 |
-
|
2607 |
-
|
2608 |
-
|
2609 |
-
|
2610 |
-
|
2611 |
-
|
2612 |
-
|
2613 |
-
|
2614 |
-
|
2615 |
-
|
2616 |
-
|
2617 |
-
|
2618 |
-
|
2619 |
-
|
2620 |
-
|
2621 |
-
|
2622 |
-
|
2623 |
-
|
2624 |
-
|
2625 |
-
|
2626 |
-
|
2627 |
-
|
2628 |
-
|
2629 |
-
|
2630 |
-
|
2631 |
-
|
2632 |
-
|
2633 |
-
|
2634 |
-
|
2635 |
-
|
2636 |
-
|
2637 |
-
|
2638 |
-
|
2639 |
-
|
2640 |
-
|
2641 |
-
|
2642 |
-
|
2643 |
-
|
2644 |
-
|
2645 |
-
|
2646 |
-
|
2647 |
-
|
2648 |
-
|
2649 |
-
|
2650 |
-
|
2651 |
-
|
2652 |
-
|
2653 |
-
|
2654 |
-
|
2655 |
-
|
2656 |
-
|
2657 |
-
|
2658 |
-
|
2659 |
-
|
2660 |
-
|
2661 |
-
|
2662 |
-
|
2663 |
-
|
2664 |
-
|
2665 |
-
|
2666 |
-
|
2667 |
-
|
2668 |
-
|
2669 |
-
|
2670 |
-
|
2671 |
-
|
2672 |
-
|
2673 |
-
|
2674 |
-
|
2675 |
-
|
2676 |
-
|
2677 |
-
|
2678 |
-
|
2679 |
-
|
2680 |
-
|
2681 |
-
|
2682 |
-
|
2683 |
-
|
2684 |
-
|
2685 |
-
|
2686 |
-
|
2687 |
-
|
2688 |
-
|
2689 |
-
|
2690 |
-
|
2691 |
-
|
2692 |
-
|
2693 |
-
|
2694 |
-
|
2695 |
-
|
2696 |
-
|
2697 |
-
|
2698 |
-
|
2699 |
-
|
2700 |
-
|
2701 |
-
|
2702 |
-
|
2703 |
-
|
2704 |
-
|
2705 |
-
|
2706 |
-
|
2707 |
-
|
2708 |
-
|
2709 |
-
|
2710 |
-
|
2711 |
-
|
2712 |
-
|
2713 |
-
|
2714 |
-
|
2715 |
-
|
2716 |
-
|
2717 |
-
|
2718 |
-
|
2719 |
-
|
2720 |
-
|
2721 |
-
|
2722 |
-
|
2723 |
-
|
2724 |
-
|
2725 |
-
|
2726 |
-
|
2727 |
-
|
2728 |
-
|
2729 |
-
|
2730 |
-
|
2731 |
-
|
2732 |
-
|
2733 |
-
|
2734 |
-
|
2735 |
-
|
2736 |
-
|
2737 |
-
|
2738 |
-
|
2739 |
-
|
2740 |
-
|
2741 |
-
|
2742 |
-
|
2743 |
-
|
2744 |
-
|
2745 |
-
|
2746 |
-
|
2747 |
-
|
2748 |
-
|
2749 |
-
|
2750 |
-
|
2751 |
-
|
2752 |
-
|
2753 |
-
|
2754 |
-
|
2755 |
-
|
2756 |
-
|
2757 |
-
|
2758 |
-
|
2759 |
-
|
2760 |
-
|
2761 |
-
|
2762 |
-
|
2763 |
-
|
2764 |
-
|
2765 |
-
|
2766 |
-
|
2767 |
-
|
2768 |
-
|
2769 |
-
|
2770 |
-
|
2771 |
-
|
2772 |
-
|
2773 |
-
|
2774 |
-
|
2775 |
-
|
2776 |
-
|
2777 |
-
|
2778 |
-
|
2779 |
-
|
2780 |
-
|
2781 |
-
|
2782 |
-
|
2783 |
-
|
2784 |
-
|
2785 |
-
|
2786 |
-
|
2787 |
-
|
2788 |
-
|
2789 |
-
|
2790 |
-
|
2791 |
-
|
2792 |
-
|
2793 |
-
|
2794 |
-
|
2795 |
-
|
2796 |
-
|
2797 |
-
|
2798 |
-
|
2799 |
-
|
2800 |
-
|
2801 |
-
|
2802 |
-
|
2803 |
-
|
2804 |
-
|
2805 |
-
|
2806 |
-
|
2807 |
-
|
2808 |
-
# λ©μΈ μ€ν
|
2809 |
-
if __name__ == "__main__":
|
2810 |
-
logger.info("AI μ§νν μ₯νΈμμ€ μμ± μμ€ν
v3.1 μμ...")
|
2811 |
-
logger.info("=" * 60)
|
2812 |
-
|
2813 |
-
# νκ²½ νμΈ
|
2814 |
-
logger.info(f"API μλν¬μΈνΈ: {API_URL}")
|
2815 |
-
logger.info(f"λͺ©ν λΆλ: {TARGET_WORDS:,}λ¨μ΄")
|
2816 |
-
logger.info(f"μκ°λΉ μ΅μ λΆλ: {MIN_WORDS_PER_WRITER:,}λ¨μ΄")
|
2817 |
-
logger.info("μ£Όμ κ°μ μ¬ν:")
|
2818 |
-
logger.info("- λΆλ λͺ©ν 8,000λ¨μ΄λ‘ μ‘°μ ")
|
2819 |
-
logger.info("- ν둬ννΈ κ°μν")
|
2820 |
-
logger.info("- λ¨μ΄ μ λΆμ‘± μ μλ μ¬μμ±")
|
2821 |
-
logger.info("- μ€μκ° μ§νλ₯ νμ")
|
2822 |
-
logger.info("- ν둬ννΈ μλ μ¦κ° κΈ°λ₯")
|
2823 |
-
logger.info("- μΈκ³Όκ΄κ³μ μΊλ¦ν° μΌκ΄μ± κ°ν")
|
2824 |
-
|
2825 |
-
if BRAVE_SEARCH_API_KEY:
|
2826 |
-
logger.info("μΉ κ²μμ΄ νμ±νλμμ΅λλ€.")
|
2827 |
-
else:
|
2828 |
-
logger.warning("μΉ κ²μμ΄ λΉνμ±νλμμ΅λλ€.")
|
2829 |
-
|
2830 |
-
if DOCX_AVAILABLE:
|
2831 |
-
logger.info("DOCX λ΄λ³΄λ΄κΈ°κ° νμ±νλμμ΅λλ€.")
|
2832 |
-
else:
|
2833 |
-
logger.warning("DOCX λ΄λ³΄λ΄κΈ°κ° λΉνμ±νλμμ΅λλ€.")
|
2834 |
-
|
2835 |
-
logger.info("=" * 60)
|
2836 |
-
|
2837 |
-
# λ°μ΄ν°λ² μ΄μ€ μ΄κΈ°ν
|
2838 |
-
logger.info("λ°μ΄ν°λ² μ΄μ€ μ΄κΈ°ν μ€...")
|
2839 |
-
NovelDatabase.init_db()
|
2840 |
-
logger.info("λ°μ΄ν°λ² μ΄μ€ μ΄κΈ°ν μλ£.")
|
2841 |
-
|
2842 |
-
# μΈν°νμ΄μ€ μμ± λ° μ€ν
|
2843 |
-
interface = create_interface()
|
2844 |
-
|
2845 |
-
interface.launch(
|
2846 |
-
server_name="0.0.0.0",
|
2847 |
-
server_port=7860,
|
2848 |
-
share=False,
|
2849 |
-
debug=True
|
2850 |
-
)</ul>
|
2851 |
-
</div>
|
2852 |
-
<div class="warning-note">
|
2853 |
-
β οΈ λΆλ λͺ©ν: κ° μκ°λΉ μ΅μ 800λ¨μ΄, μ 체 8,000λ¨μ΄ μ΄μ
|
2854 |
-
</div>
|
2855 |
-
</div>
|
2856 |
-
""")
|
2857 |
-
|
2858 |
-
# μν κ΄λ¦¬
|
2859 |
-
current_session_id = gr.State(None)
|
2860 |
-
|
2861 |
-
with gr.Row():
|
2862 |
-
with gr.Column(scale=1):
|
2863 |
-
with gr.Group(elem_classes=["input-section"]):
|
2864 |
-
query_input = gr.Textbox(
|
2865 |
-
label="μμ€ μ£Όμ / Novel Theme",
|
2866 |
-
placeholder="μ€νΈμμ€μ μ£Όμ λ₯Ό μ
λ ₯νμΈμ. μΈλ¬Όμ λ³νμ μ±μ₯μ΄ μ€μ¬μ΄ λλ μ΄μΌκΈ°...\nEnter the theme for your novella. Focus on character transformation and growth...",
|
2867 |
-
lines=4
|
2868 |
-
)
|
2869 |
-
|
2870 |
-
language_select = gr.Radio(
|
2871 |
-
choices=["Korean", "English"],
|
2872 |
-
value="Korean",
|
2873 |
-
label="μΈμ΄ / Language"
|
2874 |
-
)
|
2875 |
-
|
2876 |
-
with gr.Row():
|
2877 |
-
submit_btn = gr.Button("π μμ€ μμ± μμ", variant="primary", scale=2)
|
2878 |
-
clear_btn = gr.Button("ποΈ μ΄κΈ°ν", scale=1)
|
2879 |
-
|
2880 |
-
status_text = gr.Textbox(
|
2881 |
-
label="μν",
|
2882 |
-
interactive=False,
|
2883 |
-
value="π μ€λΉ μλ£"
|
2884 |
-
)
|
2885 |
-
|
2886 |
-
# μΈμ
κ΄λ¦¬
|
2887 |
-
with gr.Group(elem_classes=["session-section"]):
|
2888 |
-
gr.Markdown("### πΎ μ§ν μ€μΈ μΈμ
")
|
2889 |
-
session_dropdown = gr.Dropdown(
|
2890 |
-
label="μΈμ
μ ν",
|
2891 |
-
choices=[],
|
2892 |
-
interactive=True
|
2893 |
-
)
|
2894 |
-
with gr.Row():
|
2895 |
-
refresh_btn = gr.Button("π λͺ©λ‘ μλ‘κ³ μΉ¨", scale=1)
|
2896 |
-
resume_btn = gr.Button("βΆοΈ μ ν μ¬κ°", variant="secondary", scale=1)
|
2897 |
-
auto_recover_btn = gr.Button("β»οΈ μ΅κ·Ό μΈμ
볡ꡬ", scale=1)
|
2898 |
-
|
2899 |
-
with gr.Column(scale=2):
|
2900 |
-
with gr.Tab("π μ°½μ μ§ν"):
|
2901 |
-
stages_display = gr.Markdown(
|
2902 |
-
value="μ°½μ κ³Όμ μ΄ μ¬κΈ°μ νμλ©λλ€...",
|
2903 |
-
elem_id="stages-display"
|
2904 |
-
)
|
2905 |
-
|
2906 |
-
with gr.Tab("π μμ±λ μμ€"):
|
2907 |
-
novel_output = gr.Markdown(
|
2908 |
-
value="μμ±λ μμ€μ΄ μ¬κΈ°μ νμλ©λλ€...",
|
2909 |
-
elem_id="novel-output"
|
2910 |
-
)
|
2911 |
-
|
2912 |
-
with gr.Group(elem_classes=["download-section"]):
|
2913 |
-
gr.Markdown("### π₯ μμ€ λ€μ΄λ‘λ")
|
2914 |
-
with gr.Row():
|
2915 |
-
format_select = gr.Radio(
|
2916 |
-
choices=["DOCX", "TXT"],
|
2917 |
-
value="DOCX" if DOCX_AVAILABLE else "TXT",
|
2918 |
-
label="νμ"
|
2919 |
-
)
|
2920 |
-
download_btn = gr.Button("β¬οΈ λ€μ΄λ‘λ", variant="secondary")
|
2921 |
-
|
2922 |
-
download_file = gr.File(
|
2923 |
-
label="λ€μ΄λ‘λλ νμΌ",
|
2924 |
-
visible=False
|
2925 |
-
)
|
2926 |
-
|
2927 |
-
with gr.Tab("π νκ° λ³΄κ³ μ"):
|
2928 |
-
report_display = gr.Markdown(
|
2929 |
-
value="νκ° λ³΄κ³ μκ° μ¬κΈ°μ νμλ©λλ€...",
|
2930 |
-
elem_id="report-display"
|
2931 |
-
)
|
2932 |
-
|
2933 |
-
# μ¨κ²¨μ§ μν
|
2934 |
-
novel_text_state = gr.State("")
|
2935 |
-
|
2936 |
-
# μμ
|
2937 |
-
with gr.Row():
|
2938 |
-
gr.Examples(
|
2939 |
-
examples=[
|
2940 |
-
["κΈ°μ΄μνμκΈμκ° λ μ²λ
μ μμ‘΄κ³Ό μ‘΄μμ± μ°ΎκΈ°"],
|
2941 |
-
["μ€μ§ν μ€λ
λ¨μ±μ΄ μλ‘μ΄ μΆμ μλ―Έλ₯Ό μ°Ύμκ°λ μ¬μ "],
|
2942 |
-
["λμμμ μκ³¨λ‘ μ΄μ£Όν μ²λ
μ μ μκ³Ό μ±μ₯ μ΄μΌκΈ°"],
|
2943 |
-
["μΈ μΈλκ° ν¨κ» μ¬λ κ°μ‘±μ κ°λ±κ³Ό νν΄"],
|
2944 |
-
["A middle-aged woman's journey to rediscover herself after divorce"],
|
2945 |
-
["The transformation of a cynical journalist through unexpected encounters"],
|
2946 |
-
["μμ μμ μ μ΄μνλ λ
ΈλΆλΆμ λ§μ§λ§ 1λ
"],
|
2947 |
-
["AI μλμ μΌμ리λ₯Ό μμ λ²μκ°μ μλ‘μ΄ λμ "],
|
2948 |
-
["μ¬κ°λ°λ‘ μ¬λΌμ Έκ°λ λλ€μμμ λ§μ§λ§ κ³μ "]
|
2949 |
-
],
|
2950 |
-
inputs=query_input,
|
2951 |
-
label="π‘ μ£Όμ μμ"
|
2952 |
-
)
|
2953 |
-
|
2954 |
-
# μ΄λ²€νΈ νΈλ€λ¬
|
2955 |
-
def refresh_sessions():
|
2956 |
-
try:
|
2957 |
-
sessions = get_active_sessions("Korean")
|
2958 |
-
return gr.update(choices=sessions)
|
2959 |
-
except Exception as e:
|
2960 |
-
logger.error(f"Error refreshing sessions: {str(e)}")
|
2961 |
-
logger.error(f"Full error: {e}", exc_info=True) # μ 체 μ€ν νΈλ μ΄μ€ λ‘κΉ
|
2962 |
-
return gr.update(choices=[])
|
2963 |
-
|
2964 |
-
def handle_auto_recover(language):
|
2965 |
-
session_id, message = auto_recover_session(language)
|
2966 |
-
return session_id, message
|
2967 |
-
|
2968 |
-
def update_displays(stages_md, novel_md, status, session_id):
|
2969 |
-
"""λͺ¨λ λμ€νλ μ΄ μ
λ°μ΄νΈ"""
|
2970 |
-
# νκ° λ³΄κ³ μ κ°μ Έμ€κΈ°
|
2971 |
-
report = ""
|
2972 |
-
if session_id:
|
2973 |
-
session = NovelDatabase.get_session(session_id)
|
2974 |
-
if session and session.get('literary_report'):
|
2975 |
-
report = session['literary_report']
|
2976 |
-
|
2977 |
-
return stages_md, novel_md, status, session_id, report
|
2978 |
-
|
2979 |
-
# μ΄λ²€νΈ μ°κ²°
|
2980 |
-
submit_btn.click(
|
2981 |
-
fn=process_query,
|
2982 |
-
inputs=[query_input, language_select, current_session_id],
|
2983 |
-
outputs=[stages_display, novel_output, status_text, current_session_id]
|
2984 |
-
).then(
|
2985 |
-
fn=lambda s, n, st, sid: (s, n, st, sid, NovelDatabase.get_session(sid).get('literary_report', '') if sid and NovelDatabase.get_session(sid) else ''),
|
2986 |
-
inputs=[stages_display, novel_output, status_text, current_session_id],
|
2987 |
-
outputs=[stages_display, novel_output, status_text, current_session_id, report_display]
|
2988 |
-
)
|
2989 |
-
|
2990 |
-
novel_output.change(
|
2991 |
-
fn=lambda x: x,
|
2992 |
-
inputs=[novel_output],
|
2993 |
-
outputs=[novel_text_state]
|
2994 |
-
)
|
2995 |
-
|
2996 |
-
resume_btn.click(
|
2997 |
-
fn=lambda x: x.split("...")[0] if x and "..." in x else x,
|
2998 |
-
inputs=[session_dropdown],
|
2999 |
-
outputs=[current_session_id]
|
3000 |
-
).then(
|
3001 |
-
fn=resume_session,
|
3002 |
-
inputs=[current_session_id, language_select],
|
3003 |
-
outputs=[stages_display, novel_output, status_text, current_session_id]
|
3004 |
-
).then(
|
3005 |
-
fn=lambda s, n, st, sid: (s, n, st, sid, NovelDatabase.get_session(sid).get('literary_report', '') if sid and NovelDatabase.get_session(sid) else ''),
|
3006 |
-
inputs=[stages_display, novel_output, status_text, current_session_id],
|
3007 |
-
outputs=[stages_display, novel_output, status_text, current_session_id, report_display]
|
3008 |
-
)
|
3009 |
-
|
3010 |
-
auto_recover_btn.click(
|
3011 |
-
fn=handle_auto_recover,
|
3012 |
-
inputs=[language_select],
|
3013 |
-
outputs=[current_session_id, status_text]
|
3014 |
-
).then(
|
3015 |
-
fn=resume_session,
|
3016 |
-
inputs=[current_session_id, language_select],
|
3017 |
-
outputs=[stages_display, novel_output, status_text, current_session_id]
|
3018 |
-
).then(
|
3019 |
-
fn=lambda s, n, st, sid: (s, n, st, sid, NovelDatabase.get_session(sid).get('literary_report', '') if sid and NovelDatabase.get_session(sid) else ''),
|
3020 |
-
inputs=[stages_display, novel_output, status_text, current_session_id],
|
3021 |
-
outputs=[stages_display, novel_output, status_text, current_session_id, report_display]
|
3022 |
-
)
|
3023 |
-
|
3024 |
-
refresh_btn.click(
|
3025 |
-
fn=refresh_sessions,
|
3026 |
-
outputs=[session_dropdown]
|
3027 |
-
)
|
3028 |
-
|
3029 |
-
clear_btn.click(
|
3030 |
-
fn=lambda: ("", "", "π μ€λΉ μλ£", "", None, ""),
|
3031 |
-
outputs=[stages_display, novel_output, status_text, novel_text_state, current_session_id, report_display]
|
3032 |
-
)
|
3033 |
-
|
3034 |
-
def handle_download(format_type, language, session_id, novel_text):
|
3035 |
-
if not session_id or not novel_text:
|
3036 |
-
return gr.update(visible=False)
|
3037 |
-
|
3038 |
-
file_path = download_novel(novel_text, format_type, language, session_id)
|
3039 |
-
if file_path:
|
3040 |
-
return gr.update(value=file_path, visible=True)
|
3041 |
-
else:
|
3042 |
-
return gr.update(visible=False)
|
3043 |
-
|
3044 |
-
download_btn.click(
|
3045 |
-
fn=handle_download,
|
3046 |
-
inputs=[format_select, language_select, current_session_id, novel_text_state],
|
3047 |
-
outputs=[download_file]
|
3048 |
-
)
|
3049 |
-
|
3050 |
-
# μμ μ μΈμ
λ‘λ
|
3051 |
-
interface.load(
|
3052 |
-
fn=refresh_sessions,
|
3053 |
-
outputs=[session_dropdown]
|
3054 |
-
)
|
3055 |
-
|
3056 |
-
return interface
|
3057 |
|
3058 |
|
3059 |
# λ©μΈ μ€ν
|
3060 |
if __name__ == "__main__":
|
3061 |
-
|
3062 |
-
|
3063 |
-
|
3064 |
-
|
3065 |
-
|
3066 |
-
|
3067 |
-
|
3068 |
-
|
3069 |
-
|
3070 |
-
|
3071 |
-
|
3072 |
-
|
3073 |
-
|
3074 |
-
|
3075 |
-
|
3076 |
-
|
3077 |
-
|
3078 |
-
|
3079 |
-
|
3080 |
-
|
3081 |
-
|
3082 |
-
|
3083 |
-
|
3084 |
-
|
3085 |
-
|
3086 |
-
|
3087 |
-
|
3088 |
-
|
3089 |
-
|
3090 |
-
|
3091 |
-
|
3092 |
-
|
3093 |
-
|
3094 |
-
|
3095 |
-
|
3096 |
-
|
3097 |
-
|
3098 |
-
|
3099 |
-
|
3100 |
-
|
3101 |
-
|
|
|
2569 |
|
2570 |
# Gradio μΈν°νμ΄μ€ μμ±
|
2571 |
def create_interface():
|
2572 |
+
with gr.Blocks(css=custom_css, title="AI μ§νν μ₯νΈμμ€ μμ± μμ€ν
v3.1") as interface:
|
2573 |
+
gr.HTML("""
|
2574 |
+
<div class="main-header">
|
2575 |
+
<h1 style="font-size: 2.5em; margin-bottom: 10px;">
|
2576 |
+
π AI μ§νν μ₯νΈμμ€ μμ± μμ€ν
v3.1
|
2577 |
+
</h1>
|
2578 |
+
<h3 style="color: #ddd; margin-bottom: 20px;">
|
2579 |
+
λͺ©ν λΆλ λ¬μ±μ μν μ΅μ ν λ²μ
|
2580 |
+
</h3>
|
2581 |
+
<p style="font-size: 1.1em; color: #eee; max-width: 800px; margin: 0 auto;">
|
2582 |
+
10κ°μ μ κΈ°μ μΌλ‘ μ°κ²°λ λ¨κ³λ₯Ό ν΅ν΄ νλμ μμ ν μ΄μΌκΈ°λ₯Ό λ§λ€μ΄λ
λλ€.
|
2583 |
+
<br>
|
2584 |
+
κ° λ¨κ³λ μ΄μ λ¨κ³μ νμ°μ κ²°κ³Όλ‘ μ΄μ΄μ§λ©°, μΈλ¬Όμ λ³νμ μ±μ₯μ μΆμ ν©λλ€.
|
2585 |
+
</p>
|
2586 |
+
<div class="progress-note">
|
2587 |
+
β‘ λ°λ³΅μ΄ μλ μΆμ , μνμ΄ μλ μ§νμ ν΅ν μ§μ ν μ₯νΈ μμ¬
|
2588 |
+
</div>
|
2589 |
+
<div class="improvement-note">
|
2590 |
+
π v3.1 ν΅μ¬ κ°μ μ¬ν:
|
2591 |
+
<ul style="text-align: left; margin: 10px auto; max-width: 600px;">
|
2592 |
+
<li>λͺ©ν λ¨μ΄ μ 8,000λ¨μ΄λ‘ μ‘°μ </li>
|
2593 |
+
<li>ν둬ννΈ κ°μνλ‘ μμ± κ³΅κ° ν보</li>
|
2594 |
+
<li>λ¨μ΄ μ λΆμ‘± μ μλ μ¬μμ±</li>
|
2595 |
+
<li>μ€μκ° μ§νλ₯ νμ</li>
|
2596 |
+
<li>λΆλ λ―Έλ¬ κ²½κ³ μμ€ν
</li>
|
2597 |
+
<li>ν둬ννΈ μλ μ¦κ° κΈ°λ₯</li>
|
2598 |
+
<li>μΈκ³Όκ΄κ³μ μΊλ¦ν° μΌκ΄μ± κ°ν</li>
|
2599 |
+
</ul>
|
2600 |
+
</div>
|
2601 |
+
<div class="warning-note">
|
2602 |
+
β οΈ λΆλ λͺ©ν: κ° μκ°λΉ μ΅μ 800λ¨μ΄, μ 체 8,000λ¨μ΄ μ΄μ
|
2603 |
+
</div>
|
2604 |
+
</div>
|
2605 |
+
""")
|
2606 |
+
|
2607 |
+
# μν κ΄λ¦¬
|
2608 |
+
current_session_id = gr.State(None)
|
2609 |
+
|
2610 |
+
with gr.Row():
|
2611 |
+
with gr.Column(scale=1):
|
2612 |
+
with gr.Group(elem_classes=["input-section"]):
|
2613 |
+
query_input = gr.Textbox(
|
2614 |
+
label="μμ€ μ£Όμ / Novel Theme",
|
2615 |
+
placeholder="μ€νΈμμ€μ μ£Όμ λ₯Ό μ
λ ₯νμΈμ. μΈλ¬Όμ λ³νμ μ±μ₯μ΄ μ€μ¬μ΄ λλ μ΄μΌκΈ°...\nEnter the theme for your novella. Focus on character transformation and growth...",
|
2616 |
+
lines=4
|
2617 |
+
)
|
2618 |
+
|
2619 |
+
language_select = gr.Radio(
|
2620 |
+
choices=["Korean", "English"],
|
2621 |
+
value="Korean",
|
2622 |
+
label="μΈμ΄ / Language"
|
2623 |
+
)
|
2624 |
+
|
2625 |
+
with gr.Row():
|
2626 |
+
submit_btn = gr.Button("π μμ€ μμ± μμ", variant="primary", scale=2)
|
2627 |
+
clear_btn = gr.Button("ποΈ μ΄κΈ°ν", scale=1)
|
2628 |
+
|
2629 |
+
status_text = gr.Textbox(
|
2630 |
+
label="μν",
|
2631 |
+
interactive=False,
|
2632 |
+
value="π μ€λΉ μλ£"
|
2633 |
+
)
|
2634 |
+
|
2635 |
+
# μΈμ
κ΄λ¦¬
|
2636 |
+
with gr.Group(elem_classes=["session-section"]):
|
2637 |
+
gr.Markdown("### πΎ μ§ν μ€μΈ μΈμ
")
|
2638 |
+
session_dropdown = gr.Dropdown(
|
2639 |
+
label="μΈμ
μ ν",
|
2640 |
+
choices=[],
|
2641 |
+
interactive=True
|
2642 |
+
)
|
2643 |
+
with gr.Row():
|
2644 |
+
refresh_btn = gr.Button("π λͺ©λ‘ μλ‘κ³ μΉ¨", scale=1)
|
2645 |
+
resume_btn = gr.Button("βΆοΈ μ ν μ¬κ°", variant="secondary", scale=1)
|
2646 |
+
auto_recover_btn = gr.Button("β»οΈ μ΅κ·Ό μΈμ
볡ꡬ", scale=1)
|
2647 |
+
|
2648 |
+
with gr.Column(scale=2):
|
2649 |
+
with gr.Tab("π μ°½μ μ§ν"):
|
2650 |
+
stages_display = gr.Markdown(
|
2651 |
+
value="μ°½μ κ³Όμ μ΄ μ¬κΈ°μ νμλ©λλ€...",
|
2652 |
+
elem_id="stages-display"
|
2653 |
+
)
|
2654 |
+
|
2655 |
+
with gr.Tab("π μμ±λ μμ€"):
|
2656 |
+
novel_output = gr.Markdown(
|
2657 |
+
value="μμ±λ μμ€μ΄ μ¬κΈ°μ νμλ©λλ€...",
|
2658 |
+
elem_id="novel-output"
|
2659 |
+
)
|
2660 |
+
|
2661 |
+
with gr.Group(elem_classes=["download-section"]):
|
2662 |
+
gr.Markdown("### π₯ μμ€ λ€μ΄λ‘λ")
|
2663 |
+
with gr.Row():
|
2664 |
+
format_select = gr.Radio(
|
2665 |
+
choices=["DOCX", "TXT"],
|
2666 |
+
value="DOCX" if DOCX_AVAILABLE else "TXT",
|
2667 |
+
label="νμ"
|
2668 |
+
)
|
2669 |
+
download_btn = gr.Button("β¬οΈ λ€μ΄λ‘λ", variant="secondary")
|
2670 |
+
|
2671 |
+
download_file = gr.File(
|
2672 |
+
label="λ€μ΄λ‘λλ νμΌ",
|
2673 |
+
visible=False
|
2674 |
+
)
|
2675 |
+
|
2676 |
+
with gr.Tab("π νκ° λ³΄κ³ μ"):
|
2677 |
+
report_display = gr.Markdown(
|
2678 |
+
value="νκ° λ³΄κ³ μκ° μ¬κΈ°μ νμλ©λλ€...",
|
2679 |
+
elem_id="report-display"
|
2680 |
+
)
|
2681 |
+
|
2682 |
+
# μ¨κ²¨μ§ μν
|
2683 |
+
novel_text_state = gr.State("")
|
2684 |
+
|
2685 |
+
# μμ
|
2686 |
+
with gr.Row():
|
2687 |
+
gr.Examples(
|
2688 |
+
examples=[
|
2689 |
+
["κΈ°μ΄μνμκΈμκ° λ μ²λ
μ μμ‘΄κ³Ό μ‘΄μμ± μ°ΎκΈ°"],
|
2690 |
+
["μ€μ§ν μ€λ
λ¨μ±μ΄ μλ‘μ΄ μΆμ μλ―Έλ₯Ό μ°Ύμκ°λ μ¬μ "],
|
2691 |
+
["λμμμ μκ³¨λ‘ μ΄μ£Όν μ²λ
μ μ μκ³Ό μ±μ₯ μ΄μΌκΈ°"],
|
2692 |
+
["μΈ μΈλκ° ν¨κ» μ¬λ κ°μ‘±μ κ°λ±κ³Ό νν΄"],
|
2693 |
+
["A middle-aged woman's journey to rediscover herself after divorce"],
|
2694 |
+
["The transformation of a cynical journalist through unexpected encounters"],
|
2695 |
+
["μμ μμ μ μ΄μνλ λ
ΈλΆλΆμ λ§μ§λ§ 1λ
"],
|
2696 |
+
["AI μλμ μΌμ리λ₯Ό μμ λ²μκ°μ μλ‘μ΄ λμ "],
|
2697 |
+
["μ¬κ°λ°λ‘ μ¬λΌμ Έκ°λ λλ€μμμ λ§μ§λ§ κ³μ "]
|
2698 |
+
],
|
2699 |
+
inputs=query_input,
|
2700 |
+
label="π‘ μ£Όμ μμ"
|
2701 |
+
)
|
2702 |
+
|
2703 |
+
# μ΄λ²€νΈ νΈλ€λ¬
|
2704 |
+
def refresh_sessions():
|
2705 |
+
try:
|
2706 |
+
sessions = get_active_sessions("Korean")
|
2707 |
+
return gr.update(choices=sessions)
|
2708 |
+
except Exception as e:
|
2709 |
+
logger.error(f"Error refreshing sessions: {str(e)}")
|
2710 |
+
logger.error(f"Full error: {e}", exc_info=True) # μ 체 μ€ν νΈλ μ΄μ€ λ‘κΉ
|
2711 |
+
return gr.update(choices=[])
|
2712 |
+
|
2713 |
+
def handle_auto_recover(language):
|
2714 |
+
session_id, message = auto_recover_session(language)
|
2715 |
+
return session_id, message
|
2716 |
+
|
2717 |
+
def update_displays(stages_md, novel_md, status, session_id):
|
2718 |
+
"""λͺ¨λ λμ€νλ μ΄ μ
λ°μ΄νΈ"""
|
2719 |
+
# νκ° λ³΄κ³ μ κ°μ Έμ€κΈ°
|
2720 |
+
report = ""
|
2721 |
+
if session_id:
|
2722 |
+
session = NovelDatabase.get_session(session_id)
|
2723 |
+
if session and session.get('literary_report'):
|
2724 |
+
report = session['literary_report']
|
2725 |
+
|
2726 |
+
return stages_md, novel_md, status, session_id, report
|
2727 |
+
|
2728 |
+
# μ΄λ²€νΈ μ°κ²°
|
2729 |
+
submit_btn.click(
|
2730 |
+
fn=process_query,
|
2731 |
+
inputs=[query_input, language_select, current_session_id],
|
2732 |
+
outputs=[stages_display, novel_output, status_text, current_session_id]
|
2733 |
+
).then(
|
2734 |
+
fn=lambda s, n, st, sid: (s, n, st, sid, NovelDatabase.get_session(sid).get('literary_report', '') if sid and NovelDatabase.get_session(sid) else ''),
|
2735 |
+
inputs=[stages_display, novel_output, status_text, current_session_id],
|
2736 |
+
outputs=[stages_display, novel_output, status_text, current_session_id, report_display]
|
2737 |
+
)
|
2738 |
+
|
2739 |
+
novel_output.change(
|
2740 |
+
fn=lambda x: x,
|
2741 |
+
inputs=[novel_output],
|
2742 |
+
outputs=[novel_text_state]
|
2743 |
+
)
|
2744 |
+
|
2745 |
+
resume_btn.click(
|
2746 |
+
fn=lambda x: x.split("...")[0] if x and "..." in x else x,
|
2747 |
+
inputs=[session_dropdown],
|
2748 |
+
outputs=[current_session_id]
|
2749 |
+
).then(
|
2750 |
+
fn=resume_session,
|
2751 |
+
inputs=[current_session_id, language_select],
|
2752 |
+
outputs=[stages_display, novel_output, status_text, current_session_id]
|
2753 |
+
).then(
|
2754 |
+
fn=lambda s, n, st, sid: (s, n, st, sid, NovelDatabase.get_session(sid).get('literary_report', '') if sid and NovelDatabase.get_session(sid) else ''),
|
2755 |
+
inputs=[stages_display, novel_output, status_text, current_session_id],
|
2756 |
+
outputs=[stages_display, novel_output, status_text, current_session_id, report_display]
|
2757 |
+
)
|
2758 |
+
|
2759 |
+
auto_recover_btn.click(
|
2760 |
+
fn=handle_auto_recover,
|
2761 |
+
inputs=[language_select],
|
2762 |
+
outputs=[current_session_id, status_text]
|
2763 |
+
).then(
|
2764 |
+
fn=resume_session,
|
2765 |
+
inputs=[current_session_id, language_select],
|
2766 |
+
outputs=[stages_display, novel_output, status_text, current_session_id]
|
2767 |
+
).then(
|
2768 |
+
fn=lambda s, n, st, sid: (s, n, st, sid, NovelDatabase.get_session(sid).get('literary_report', '') if sid and NovelDatabase.get_session(sid) else ''),
|
2769 |
+
inputs=[stages_display, novel_output, status_text, current_session_id],
|
2770 |
+
outputs=[stages_display, novel_output, status_text, current_session_id, report_display]
|
2771 |
+
)
|
2772 |
+
|
2773 |
+
refresh_btn.click(
|
2774 |
+
fn=refresh_sessions,
|
2775 |
+
outputs=[session_dropdown]
|
2776 |
+
)
|
2777 |
+
|
2778 |
+
clear_btn.click(
|
2779 |
+
fn=lambda: ("", "", "π μ€λΉ μλ£", "", None, ""),
|
2780 |
+
outputs=[stages_display, novel_output, status_text, novel_text_state, current_session_id, report_display]
|
2781 |
+
)
|
2782 |
+
|
2783 |
+
def handle_download(format_type, language, session_id, novel_text):
|
2784 |
+
if not session_id or not novel_text:
|
2785 |
+
return gr.update(visible=False)
|
2786 |
+
|
2787 |
+
file_path = download_novel(novel_text, format_type, language, session_id)
|
2788 |
+
if file_path:
|
2789 |
+
return gr.update(value=file_path, visible=True)
|
2790 |
+
else:
|
2791 |
+
return gr.update(visible=False)
|
2792 |
+
|
2793 |
+
download_btn.click(
|
2794 |
+
fn=handle_download,
|
2795 |
+
inputs=[format_select, language_select, current_session_id, novel_text_state],
|
2796 |
+
outputs=[download_file]
|
2797 |
+
)
|
2798 |
+
|
2799 |
+
# μμ μ μΈμ
λ‘λ
|
2800 |
+
interface.load(
|
2801 |
+
fn=refresh_sessions,
|
2802 |
+
outputs=[session_dropdown]
|
2803 |
+
)
|
2804 |
+
|
2805 |
+
return interface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2806 |
|
2807 |
|
2808 |
# λ©μΈ μ€ν
|
2809 |
if __name__ == "__main__":
|
2810 |
+
logger.info("AI μ§νν μ₯νΈμμ€ μμ± μμ€ν
v3.1 μμ...")
|
2811 |
+
logger.info("=" * 60)
|
2812 |
+
|
2813 |
+
# νκ²½ νμΈ
|
2814 |
+
logger.info(f"API μλν¬μΈνΈ: {API_URL}")
|
2815 |
+
logger.info(f"λͺ©ν λΆλ: {TARGET_WORDS:,}λ¨μ΄")
|
2816 |
+
logger.info(f"μκ°λΉ μ΅μ λΆλ: {MIN_WORDS_PER_WRITER:,}λ¨μ΄")
|
2817 |
+
logger.info("μ£Όμ κ°μ μ¬ν:")
|
2818 |
+
logger.info("- λΆλ λͺ©ν 8,000λ¨μ΄λ‘ μ‘°μ ")
|
2819 |
+
logger.info("- ν둬ννΈ κ°μν")
|
2820 |
+
logger.info("- λ¨μ΄ μ λΆμ‘± μ μλ μ¬μμ±")
|
2821 |
+
logger.info("- μ€μκ° μ§νλ₯ νμ")
|
2822 |
+
logger.info("- ν둬ννΈ μλ μ¦κ° κΈ°λ₯")
|
2823 |
+
logger.info("- μΈκ³Όκ΄κ³μ μΊλ¦ν° μΌκ΄μ± κ°ν")
|
2824 |
+
|
2825 |
+
if BRAVE_SEARCH_API_KEY:
|
2826 |
+
logger.info("μΉ κ²μμ΄ νμ±νλμμ΅λλ€.")
|
2827 |
+
else:
|
2828 |
+
logger.warning("μΉ κ²μμ΄ λΉνμ±νλμμ΅λλ€.")
|
2829 |
+
|
2830 |
+
if DOCX_AVAILABLE:
|
2831 |
+
logger.info("DOCX λ΄λ³΄λ΄κΈ°κ° νμ±νλμμ΅λλ€.")
|
2832 |
+
else:
|
2833 |
+
logger.warning("DOCX λ΄λ³΄λ΄κΈ°κ° λΉνμ±νλμμ΅λλ€.")
|
2834 |
+
|
2835 |
+
logger.info("=" * 60)
|
2836 |
+
|
2837 |
+
# λ°μ΄ν°λ² μ΄μ€ μ΄κΈ°ν
|
2838 |
+
logger.info("λ°μ΄ν°λ² μ΄μ€ μ΄κΈ°ν μ€...")
|
2839 |
+
NovelDatabase.init_db()
|
2840 |
+
logger.info("λ°μ΄ν°λ² μ΄μ€ μ΄κΈ°ν μλ£.")
|
2841 |
+
|
2842 |
+
# μΈν°νμ΄μ€ μμ± λ° μ€ν
|
2843 |
+
interface = create_interface()
|
2844 |
+
|
2845 |
+
interface.launch(
|
2846 |
+
server_name="0.0.0.0",
|
2847 |
+
server_port=7860,
|
2848 |
+
share=False,
|
2849 |
+
debug=True
|
2850 |
+
)
|