File size: 5,057 Bytes
7b7257a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"""Single Chat ํƒญ UI"""

import gradio as gr
from typing import Any

from models.model_registry import get_all_models, get_model_info
from characters import get_character_loader, build_system_prompt
from utils import parse_thinking_response, format_thinking_for_display


def create_chat_tab(
    model_manager: Any = None,
    use_mock: bool = False,
):
    """Single Chat ํƒญ ์ƒ์„ฑ"""

    # ๋ฐ์ดํ„ฐ ๋กœ๋”
    char_loader = get_character_loader()

    # ๋ชจ๋ธ ๋ชฉ๋ก
    all_models = get_all_models()
    model_choices = [(f"{get_model_info(m).get('description', m)}", m) for m in all_models]

    # ์บ๋ฆญํ„ฐ ๋ชฉ๋ก
    characters = char_loader.get_character_names()

    # ============================================================
    # UI ๊ตฌ์„ฑ
    # ============================================================

    gr.Markdown("## ๋‹จ์ผ ๋ชจ๋ธ ์ฑ„ํŒ…")
    gr.Markdown("์„ ํƒํ•œ ๋ชจ๋ธ๊ณผ ์บ๋ฆญํ„ฐ๋กœ ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆ ๋ณด์„ธ์š”.")

    with gr.Row():
        with gr.Column(scale=1):
            model_dropdown = gr.Dropdown(
                choices=model_choices,
                value=all_models[0] if all_models else None,
                label="๋ชจ๋ธ ์„ ํƒ",
            )
        with gr.Column(scale=1):
            character_dropdown = gr.Dropdown(
                choices=characters,
                value=characters[0] if characters else None,
                label="์บ๋ฆญํ„ฐ ์„ ํƒ",
            )

    # ์ฑ„ํŒ… ์˜์—ญ
    chatbot = gr.Chatbot(
        label="๋Œ€ํ™”",
        height=400,
        type="messages",
    )

    with gr.Accordion("Thinking Process (๋งˆ์ง€๋ง‰ ์‘๋‹ต)", open=False):
        thinking_display = gr.Markdown("*(์‘๋‹ต ์ƒ์„ฑ ํ›„ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค)*")

    with gr.Row():
        user_input = gr.Textbox(
            label="๋ฉ”์‹œ์ง€ ์ž…๋ ฅ",
            placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”...",
            lines=2,
            scale=4,
        )
        send_btn = gr.Button("์ „์†ก", variant="primary", scale=1)

    with gr.Row():
        clear_btn = gr.Button("๋Œ€ํ™” ์ดˆ๊ธฐํ™”")

    metadata_display = gr.Markdown("")

    # ============================================================
    # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
    # ============================================================

    def respond(
        model_id: str,
        character: str,
        message: str,
        history: list,
    ):
        """์‘๋‹ต ์ƒ์„ฑ"""
        if not message.strip():
            return history, "", "*(๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”)*", ""

        # ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ๊ตฌ์„ฑ
        messages = []
        for msg in history:
            if msg["role"] == "user":
                messages.append({"role": "user", "content": msg["content"]})
            elif msg["role"] == "assistant":
                # Thinking ์ œ๊ฑฐํ•œ ํด๋ฆฐ ์‘๋‹ต๋งŒ ํžˆ์Šคํ† ๋ฆฌ์—
                _, clean = parse_thinking_response(msg["content"])
                messages.append({"role": "assistant", "content": clean})

        messages.append({"role": "user", "content": message})

        system_prompt = build_system_prompt(character)

        # Mock ๋˜๋Š” ์‹ค์ œ ์ถ”๋ก 
        if use_mock or model_manager is None:
            response_full = f"<think>\n{character}๋กœ์„œ ์ƒ๊ฐํ•ด๋ณด๋ฉด...\n</think>\n\n์•ˆ๋…•~ ๋ฐ˜๊ฐ€์›Œ! (Mock Response)"
            meta = {"latency_s": 0.5, "output_tokens": 30}
        else:
            try:
                response_full, meta = model_manager.generate_response(
                    model_id, messages, system_prompt
                )
            except Exception as e:
                response_full = f"*Error: {str(e)}*"
                meta = {"latency_s": 0, "output_tokens": 0}

        # Thinking ํŒŒ์‹ฑ
        thinking, clean_response = parse_thinking_response(response_full)

        # ํžˆ์Šคํ† ๋ฆฌ ์—…๋ฐ์ดํŠธ
        history.append({"role": "user", "content": message})
        history.append({"role": "assistant", "content": response_full})

        # ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
        meta_str = f"โฑ๏ธ {meta.get('latency_s', 0):.2f}s | {meta.get('output_tokens', 0)} tokens"

        return (
            history,
            "",  # ์ž…๋ ฅ ์ดˆ๊ธฐํ™”
            format_thinking_for_display(thinking) if thinking else "*No thinking*",
            meta_str,
        )

    def clear_chat():
        """๋Œ€ํ™” ์ดˆ๊ธฐํ™”"""
        return [], "", "*(์‘๋‹ต ์ƒ์„ฑ ํ›„ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค)*", ""

    # ============================================================
    # ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉ
    # ============================================================

    send_btn.click(
        fn=respond,
        inputs=[model_dropdown, character_dropdown, user_input, chatbot],
        outputs=[chatbot, user_input, thinking_display, metadata_display],
    )

    user_input.submit(
        fn=respond,
        inputs=[model_dropdown, character_dropdown, user_input, chatbot],
        outputs=[chatbot, user_input, thinking_display, metadata_display],
    )

    clear_btn.click(
        fn=clear_chat,
        outputs=[chatbot, user_input, thinking_display, metadata_display],
    )