File size: 8,300 Bytes
b3d8fba
0b7a4cc
 
 
55d5a9b
0b7a4cc
 
4b09aa4
0e54374
 
 
 
94ed0a9
15c7b7e
0b7a4cc
 
 
 
15c7b7e
f8305c7
0b7a4cc
 
 
eef72bf
15c7b7e
 
0e54374
15c7b7e
 
 
 
 
 
 
 
0b7a4cc
15c7b7e
 
 
 
f8305c7
15c7b7e
 
 
 
0e54374
15c7b7e
0e54374
 
 
f8305c7
 
 
0e54374
d385a22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313f4e3
 
 
 
 
 
 
 
 
 
 
 
cc23ac3
 
d385a22
cc23ac3
313f4e3
 
0e54374
313f4e3
 
 
 
 
 
 
 
 
 
 
 
 
0e54374
313f4e3
 
 
0e54374
 
 
 
 
0b37ec9
3a6190a
 
 
4b09aa4
 
e40f9e3
 
0535a78
e40f9e3
0535a78
e40f9e3
0b7a4cc
e40f9e3
 
0535a78
 
 
0b7a4cc
e40f9e3
 
0535a78
461df80
e40f9e3
6833022
0535a78
0fc2be3
 
 
 
 
 
 
 
 
 
0535a78
399979b
0535a78
 
0e54374
15c7b7e
0535a78
15c7b7e
 
 
 
 
71641ab
15c7b7e
 
 
461df80
15c7b7e
 
 
 
0fc2be3
 
0b37ec9
0535a78
d686a4c
71641ab
02cdca3
71641ab
 
 
 
 
 
 
 
 
 
d686a4c
c85f9f8
 
 
 
6833022
0fc2be3
c85f9f8
 
 
 
0fc2be3
fdf77fb
02cdca3
 
fdf77fb
 
 
 
 
 
02cdca3
 
 
 
0b37ec9
fdf77fb
6833022
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import os
import gradio as gr
import torch
import json
import requests
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel
import torch.nn.functional as F  # Softmax ke liye

# βœ… OpenRouter API Key
OPENROUTER_API_KEY = os.getenv("OPEN_ROUTER_API_KEY")
assert OPENROUTER_API_KEY, "You must set your OpenRouter API key in Hugging Face Secrets!"

# βœ… Load Yoda Chat Model (Only Once)
MODEL_PATH = "yoda_chat_model"
ADAPTER_PATH = "yoda_chat_adapter"

device = "cuda" if torch.cuda.is_available() else "cpu"
print("πŸ”„ Loading Yoda Model...")

base_model = AutoModelForCausalLM.from_pretrained(MODEL_PATH).to(device)
model = PeftModel.from_pretrained(base_model, ADAPTER_PATH).to(device)
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)

print("βœ… Model Loaded Successfully!")

# βœ… System Prompt for OpenRouter Judge
SYSTEM_PROMPT = """
You are an impartial judge that evaluates if text was written in the style of Yoda.

An example piece of text from Yoda is:
"Do, or do not. There is no try."

Now, analyze some new text carefully and respond on if it follows the same style of Yoda.
Be critical to identify any issues in the text.

Then convert your feedback into a number between 0 and 10: 
10 if the text is written exactly in the style of Yoda, 
5 if mixed faithfulness to the style, 
0 if the text is not at all written in the style of Yoda.

The format of your response should be a JSON dictionary and nothing else:
{{"score": <score between 0 and 10>}}
"""

class LLMJudgeEvaluator:
    """Evaluates how 'Yoda-like' a text response is using OpenRouter API"""
    def __init__(self, model_name, api_key, system_prompt):
        self.model_name = model_name
        self.api_key = api_key
        self.system_prompt = system_prompt
        self.prompt_template = "Evaluate this text: {text}"

    def ask(self, user_prompt):
        # βœ… Define headers
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        
        # βœ… Define data for the request
        data = {
            "model": self.model_name,
            "messages": [{"role": "system", "content": self.system_prompt}, 
                         {"role": "user", "content": user_prompt}],
            "max_tokens": 150  # You can adjust this based on your needs
        }

        # βœ… Send the request
        response = requests.post("https://openrouter.ai/api/v1/chat/completions", headers=headers, json=data)
        
        print(f"πŸ” API Raw Response: {response.text}")  # βœ… Debugging line
        
        try:
            json_response = response.json()
            res_text = json_response["choices"][0]["message"]["content"]
        except (KeyError, IndexError, json.JSONDecodeError):
            print(f"❌ Error parsing OpenRouter response: {response.text}")
            res_text = '{"score": 0}'  # Default score 0 if error
        
        return res_text



    def score(self, text):
        """Evaluate response style and return score"""
        prompt = self.prompt_template.format(text=text)
        
        try:
            res = self.ask(prompt)
            print(f"πŸ” Judge Response: {res}")  # βœ… Debugging line
            
            # βœ… Ensure valid JSON
            if not res.strip().startswith("{"):  # If response is not JSON
                print("❌ Invalid JSON response format, returning 0.0 score")
                return 0.0
            
            res_dict = json.loads(res)  # Parse JSON response
            max_score = 10
            score = res_dict.get("score", 0) / max_score
            return max(0.0, min(score, 1.0))  # Clip score between 0-1
        
        except Exception as e:
            print(f"❌ Error scoring text: {e}")
            return 0.0  # Default score if error


# βœ… Initialize the Judge Model using OpenRouter API
model_name = "liquid/lfm-40b"  # OpenRouter model
judge = LLMJudgeEvaluator(model_name, OPENROUTER_API_KEY, SYSTEM_PROMPT)

# βœ… Define the template globally
template_without_answer = "<start_of_turn>user\n{question}<end_of_turn>\n<start_of_turn>model\n"
template_with_answer = template_without_answer + "{answer}<end_of_turn>\n"


def chat(question, max_new_tokens=32, temperature=0.7, only_answer=False):
    # 1. Construct the prompt using the template
    prompt = template_without_answer.format(question=question)
    # 2. Tokenize the text
    input_ids = tokenizer(prompt, return_tensors="pt").to(model.device)
    # 3. Feed through the model to predict the next token probabilities
    with torch.no_grad():
        outputs = model.generate(**input_ids, do_sample=True,
        max_new_tokens=max_new_tokens, temperature=temperature)
    output_tokens = outputs[0]
    if only_answer:
        output_tokens = output_tokens[input_ids['input_ids'].shape[1]:]

    # 5. Decode the tokens
    result = tokenizer.decode(output_tokens, skip_special_tokens=True)
    return result


def yoda_chat_and_judge(question):
    """Generate Yoda-style response and evaluate it"""
    
    yield """
    <div style='font-family: Arial, sans-serif; padding: 20px; background: #222; border-radius: 10px; color: white; text-align: center;'>
        <h2 style='color: #f4d03f;'>🟒 Yoda Processing...</h2>
        <p style='font-size: 18px; background: #333; padding: 10px; border-radius: 5px; color: #ecf0f1;'>
            ⏳ "Patience, you must have, my young Padawan..." πŸ€”
        </p>
    </div>
    """
    
    # βœ… Use `chat()` function instead of `generate_response()`
    response = chat(question, only_answer=True) 

    # βœ… Evaluate response
    score = judge.score(response)

    # Progress bar color based on score
    color = "#e74c3c" if score < 0.4 else "#f1c40f" if score < 0.7 else "#2ecc71"

    result = f"""
    <div style='font-family: Arial, sans-serif; padding: 20px; background: #222; border-radius: 10px; color: white;'>
        <h2 style='color: #f4d03f;'>🟒 Yoda Says:</h2>
        <p style='font-size: 18px; background: #333; padding: 10px; border-radius: 5px; color: #ecf0f1; max-height: 200px; overflow-y: auto;'>πŸ’¬ {response}</p>
        
        <h3 style='margin-top: 20px; color: #3498db;'>πŸ”΅ Yoda Style Score:</h3>
        <div style="background: #444; border-radius: 5px; padding: 5px; margin-top: 5px;">
            <div style="width: {score*100}%; background: {color}; height: 20px; border-radius: 5px;"></div>
        </div>
        <p style='font-size: 16px; color: {color}; font-weight: bold;'>Score: {score:.2f} (Closer to 1.0 = More Yoda-like!)</p>
    </div>
    """
    
    yield result  # βœ… Return final result after processing


with gr.Blocks(css="""
    .textbox, .output-box { height: 300px !important; overflow-y: auto; }
    .footer { 
        position: fixed; 
        bottom: 0; 
        width: 100%; 
        background: #222; 
        color: white; 
        padding: 10px; 
        text-align: center; 
        border-top: 2px solid #f4d03f; 
        font-size: 14px;
    }
""") as iface:
    gr.Markdown("# 🟒 Yoda Chat & Judge")
    gr.Markdown("Chat with Yoda and see how accurate his style is!")

    with gr.Row():
        inp = gr.Textbox(label="Enter your question", placeholder="Type something like Yoda would...", lines=10, elem_id="textbox")
        out = gr.HTML(value="πŸ’¬ Waiting for input...", elem_id="output-box")  # βœ… Default output box remains visible

    btn = gr.Button("Generate")
    btn.click(yoda_chat_and_judge, inputs=inp, outputs=out)

    # βœ… Fixed Footer
    # βœ… Fixed Footer with Clickable Links
    gr.HTML(
        """
        <div class='footer' style="text-align: center; padding: 10px; font-size: 16px;">
            🌟 Developed by <b>Sheema Masood</b> | May the Force Be With You! 🌌 <br>
            πŸ”— Connect with me: 
            <a href="https://github.com/SheemaMasood381" target="_blank" style="color: #f4d03f; text-decoration: none;">GitHub</a> |
            <a href="https://www.linkedin.com/in/sheema-masood/" target="_blank" style="color: #3498db; text-decoration: none;">LinkedIn</a> |
            <a href="https://www.kaggle.com/sheemamasood" target="_blank" style="color: #e74c3c; text-decoration: none;">Kaggle</a>
        </div>
        """
    )



iface.launch()