File size: 5,757 Bytes
ab76d2a
 
 
 
 
57cac61
 
034dd7d
ab76d2a
476eb45
ab76d2a
 
 
476eb45
 
57cac61
 
034dd7d
 
 
 
 
 
 
 
 
 
476eb45
ab76d2a
 
ce45729
ab76d2a
 
 
 
 
 
 
 
 
 
 
 
57cac61
ce45729
57cac61
ff147bb
ab76d2a
ce45729
57cac61
 
 
ab76d2a
034dd7d
45e2ada
2b717f1
41ad8f7
 
6768a50
1f1385b
 
6768a50
1f1385b
 
 
41ad8f7
45e2ada
 
41ad8f7
6768a50
c5dc144
41ad8f7
 
2b717f1
41ad8f7
 
 
 
f0643ac
034dd7d
41ad8f7
 
417d37b
41ad8f7
417d37b
 
 
 
 
 
 
 
 
41ad8f7
 
417d37b
45e2ada
417d37b
 
6cd0f07
476eb45
41ad8f7
 
810081f
6768a50
 
810081f
6768a50
810081f
6768a50
 
034dd7d
476eb45
810081f
476eb45
41ad8f7
fac1a49
41ad8f7
6768a50
 
810081f
6768a50
c5dc144
fac1a49
41ad8f7
6768a50
 
810081f
6768a50
476eb45
fac1a49
034dd7d
6768a50
 
a236245
c5dc144
034dd7d
6768a50
 
a236245
 
476eb45
fac1a49
 
 
810081f
fac1a49
 
 
 
476eb45
 
034dd7d
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
# coding: utf-8

# Author: Du Mingzhe (dumingzhex@gmail.com)
# Date: 2025-12-21

import os
import json
import random
import datetime
import gradio as gr
import pandas as pd
from pathlib import Path
from huggingface_hub import CommitScheduler
from huggingface_hub import InferenceClient

HF_TOKEN = os.getenv("HF_TOKEN")

# Model configuration - these should match the models launched by launch_models.py
MODELS = {
    "Local-Model-00500": "http://localhost:9000/v1",
    "Local-Model-01000": "http://localhost:9001/v1",
    "Local-Model-01500": "http://localhost:9002/v1",
    "Local-Model-02000": "http://localhost:9003/v1",
    "Local-Model-02500": "http://localhost:9004/v1",
    "Local-Model-03000": "http://localhost:9005/v1",
    "Local-Model-03500": "http://localhost:9006/v1",
}

DATA_DIR = Path("logs")
DATA_DIR.mkdir(exist_ok=True)
FEEDBACK_FILE = DATA_DIR / "feedback.jsonl"

scheduler = CommitScheduler(
    repo_id="Elfsong/arena_feedback",
    repo_type="dataset",
    folder_path=DATA_DIR,
    every=5, # Sync every 5 minutes
)

def save_feedback(model_name, history, feedback_data: gr.LikeData):
    new_entry = {
        "timestamp": datetime.datetime.now().isoformat(),
        "model_name": model_name,
        "message_index": feedback_data.index,
        "vote": feedback_data.value,
        "is_liked": feedback_data.liked,
        "conversation": history
    }
    with open(FEEDBACK_FILE, "a", encoding="utf-8") as f:
        f.write(json.dumps(new_entry, ensure_ascii=False) + "\n")
    
    print(f"Feedback logged for {model_name}")

def bot_response(user_message, history, model_name, system_message, thinking_mode, max_tokens, temperature, top_p, seed_val):
    if not user_message or user_message.strip() == "":
        yield history, ""
        return
        
    token = HF_TOKEN
    
    if model_name.startswith("Local-"):
        local_endpoint = MODELS.get(model_name)
        client = InferenceClient(base_url=local_endpoint, token="vllm-token")
    else:
        client = InferenceClient(token=token, model=model_name)
    
    history.append({"role": "user", "content": user_message})
    history.append({"role": "assistant", "content": ""})
    
    api_messages = [{"role": "system", "content": system_message + "/set think" if thinking_mode else "/set nothink"}] + history[:-1]

    try:
        stream = client.chat_completion(
            api_messages,
            max_tokens=max_tokens,
            stream=True,
            temperature=temperature,
            top_p=top_p,
            model=model_name,
            seed=seed_val,
        )
        
        response_text = ""
        for chunk in stream:
            if not chunk.choices or len(chunk.choices) == 0:
                continue
            
            token_content = chunk.choices[0].delta.content
            if token_content is not None:
                response_text += token_content
                history[-1]["content"] = response_text
                # Continuously yield update UI, while keeping input box unavailable to prevent double clicks
                yield history, gr.update(interactive=False)
            
    except Exception as e:
        # If error, display error message in assistant dialog
        history[-1]["content"] = f"**Error:** {str(e)}"
    
    # --- Final Yield: Restore input box availability and clear content ---
    yield history, gr.update(value="", interactive=True)

with gr.Blocks() as demo:
    with gr.Sidebar():
        gr.Markdown("## Configuration")
        # gr.LoginButton()

        system_msg = gr.Textbox(value="You are a helpful assistant.", label="System Prompt")
        thinking_mode = gr.Checkbox(value=False, label="Thinking Mode")
        max_t = gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max Tokens")
        temp = gr.Slider(minimum=0.0, maximum=2.0, value=0.0, step=0.05, label="Temperature")
        top_p_val = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, step=0.05, label="Top-p")
        seed_val = gr.Slider(minimum=-1, maximum=4294967295, value=random.randint(0, 4294967295), step=1, label="Seed")

    gr.Markdown("# ⚔️ Chatbot Arena")

    with gr.Row():
        # --- Model A ---
        with gr.Column():
            model_a_name = gr.Dropdown(list(MODELS.keys()), label="Model A", value=list(MODELS.keys())[0])
            chatbot_a = gr.Chatbot(label="Model A Output")
            msg_a = gr.Textbox(placeholder="Send message to Model A...", label="Model A Input")
            btn_a = gr.Button("Send to Model A")

        # --- Model B ---
        with gr.Column():
            model_b_name = gr.Dropdown(list(MODELS.keys()), label="Model B", value=list(MODELS.keys())[-1])
            chatbot_b = gr.Chatbot(label="Model B Output")
            msg_b = gr.Textbox(placeholder="Send message to Model B...", label="Model B Input")
            btn_b = gr.Button("Send to Model B")

    # --- Bind Events ---
    a_inputs = [msg_a, chatbot_a, model_a_name, system_msg, thinking_mode, max_t, temp, top_p_val, seed_val]
    msg_a.submit(bot_response, a_inputs, [chatbot_a, msg_a])
    btn_a.click(bot_response, a_inputs, [chatbot_a, msg_a])
    chatbot_a.like(save_feedback, [model_a_name, chatbot_a], None)

    b_inputs = [msg_b, chatbot_b, model_b_name, system_msg, thinking_mode, max_t, temp, top_p_val, seed_val]
    msg_b.submit(bot_response, b_inputs, [chatbot_b, msg_b])
    btn_b.click(bot_response, b_inputs, [chatbot_b, msg_b])
    chatbot_b.like(save_feedback, [model_b_name, chatbot_b], None)
    

    def clear_chats():
        return [], []

    gr.Button("🗑️ Clear Chats").click(
        fn=clear_chats, 
        inputs=None, 
        outputs=[chatbot_a, chatbot_b]
    )

if __name__ == "__main__":
    demo.launch(share=True)