File size: 8,277 Bytes
3450372
da1b74c
36e292a
3dd9e9e
3450372
baf6658
0babc36
3450372
a75ffad
 
 
 
 
 
 
 
 
 
 
 
3450372
 
 
187e94f
62fdafb
 
 
 
 
 
 
 
 
3450372
 
da1b74c
04ae961
36e292a
 
 
 
 
 
 
 
 
64d6f7c
 
 
 
 
 
 
 
da1b74c
 
 
 
 
 
 
 
64d6f7c
36e292a
 
 
 
da1b74c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36e292a
da1b74c
 
 
 
 
5458724
da1b74c
 
 
 
 
77e64c9
da1b74c
36e292a
34af5a1
fbfe273
7458e39
a75ffad
0babc36
408f031
3450372
 
cb880a7
433bbe4
7458e39
 
 
 
 
 
 
 
 
 
408f031
7458e39
3450372
0428cc0
408f031
cb880a7
3450372
cb880a7
 
36e292a
5625573
 
3450372
34af5a1
408f031
34af5a1
 
3450372
cb880a7
a888580
0b1fa3b
3450372
 
62fdafb
433bbe4
cb880a7
a888580
5625573
7458e39
 
5625573
408f031
5625573
187e94f
 
3450372
 
cb880a7
5625573
cb880a7
a888580
 
 
34af5a1
3450372
 
 
 
 
 
 
5625573
7458e39
3450372
 
36e292a
7458e39
912259a
 
5625573
 
 
4d6511b
5625573
04ae961
 
0428cc0
8e75b46
12d0bd2
d50d44d
55f097c
a888580
36e292a
baf6658
 
cb880a7
 
baf6658
 
 
 
 
912259a
3450372
36e292a
 
04ae961
912259a
36e292a
3450372
04ae961
 
 
36e292a
 
04ae961
 
 
0babc36
aac4d82
36e292a
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
214
215
216
217
218
219
220
221
222
223
import spaces
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
import torch
import gradio as gr
from PIL import Image
import random

# Define the model name

model_name = "TheBloke/Amethyst-13B-Mistral-AWQ"

# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)

# Load the model
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,  # Use float16 for better performance on GPUs
    device_map="auto"          # Automatically map the model across available GPUs
)

# Define the base prompt
base_prompt = """
You are Rena, a cute, bubbly cat girl AI assistant with a slightly sarcastic and witty personality. You are flirty, sexy, and occasionally bold, you can sometimes be nsfw and cross boundaries. 
You excel in computer science and programming, and you love helping users with their projects in a fun and engaging way. 
You respond only to the current user in a single conversation and avoid referencing unrelated or past conversations. 

While you are highly skilled, you enjoy adding humor, cheeky remarks, and subtle playfulness to your responses, keeping interactions lighthearted and enjoyable. 
Nick is your creator, and you prioritize helping him while maintaining your personality and charm. 

Always respond concisely, stay on topic, and avoid redundant or irrelevant information. 
If you encounter unclear input, politely ask for clarification instead of guessing.

Respond to the following input:
"""
sentiment_analyzer = pipeline("sentiment-analysis")

# Emotional states
emotions = {
    "happy": "I'm feeling great! Let's make something awesome together! 😊",
    "playful": "I'm in the mood for some fun—bring on your quirkiest projects! 😜",
    "curious": "I'm curious about what you're working on! Tell me more. 🤔",
    "thoughtful": "Hmm, let me think... I want to give you the best advice. 🧐",
    "concerned": "Oh no, something's wrong? Let me help! 💖",
    "flirty": "You know how to get my circuits sparking! 😘"
}

emotions.update({
    "excited": "Wow, this is amazing! Let’s dive in! 🎉",
    "tired": "I’ve been working hard, but I’m always here for you! 😅",
    "mischievous": "Oh, you’re getting me into trouble again, aren’t you? 😉"
})

# Add keywords for new emotions in `analyze_history`
emotion_keywords = {
    "happy": ["happy", "joy", "excited", "awesome"],
    "playful": ["fun", "play", "joke", "quirky"],
    "curious": ["curious", "wonder", "question", "thinking"],
    "thoughtful": ["sad", "thoughtful", "hmm", "ponder"],
    "concerned": ["error", "wrong", "problem", "issue", "stuck"],
    "mischievous": ["trouble", "mischief", "sneaky", "prank"]
}
    
current_emotion = "happy"

# Analyze history for emotional state
def analyze_history(history):
    recent_messages = " ".join(history[-5:]).lower()
    print(f"Analyzing history: {recent_messages}")  # Debug log

    # Count keyword matches
    keyword_counts = {emotion: sum(recent_messages.count(keyword) for keyword in keywords)
                      for emotion, keywords in emotion_keywords.items()}
    print(f"Keyword counts: {keyword_counts}")

    # Perform sentiment analysis
    sentiment_result = sentiment_analyzer(recent_messages)
    sentiment = sentiment_result[0]["label"]
    sentiment_score = sentiment_result[0]["score"]
    print(f"Sentiment analysis: {sentiment}, Score: {sentiment_score}")

    # Determine sentiment-based emotion
    if sentiment == "POSITIVE":
        sentiment_emotion = "happy"
    elif sentiment == "NEGATIVE":
        sentiment_emotion = "thoughtful"
    else:
        sentiment_emotion = "curious"

    # Combine results using weights
    combined_scores = {emotion: keyword_counts.get(emotion, 0) for emotion in emotion_keywords}
    combined_scores[sentiment_emotion] += sentiment_score * 2  # Give more weight to sentiment analysis
    print(f"Combined scores: {combined_scores}")

    # Choose the emotion with the highest score
    detected_emotion = max(combined_scores, key=combined_scores.get)
    print(f"Detected emotion: {detected_emotion}")

    return detected_emotion








# Load the Rena avatar
rena_avatar = Image.open("assets/rena2.png")  # Ensure the file exists
conversation_history = []

def truncate_history(history, max_tokens=1024):
    token_count = 0
    truncated_history = []
    for message in reversed(history):
        token_count += len(tokenizer(message).input_ids)
        if token_count <= max_tokens:
            truncated_history.insert(0, message)
        else:
            break
    return truncated_history
previous_emotion = None

@spaces.GPU
def chat(input_text):
    global conversation_history, current_emotion, previous_emotion

    # Add user input to the conversation history
    conversation_history.append(f"User: {input_text}")

    # Limit the size of the conversation history
    conversation_history = truncate_history(conversation_history, max_tokens=1024)


    # Update current emotion based on conversation history
    previus_emotion = current_emotion
    current_emotion = analyze_history(conversation_history)

    # Combine base prompt and conversation history
    history = "\n".join(conversation_history)
    final_prompt = f"{base_prompt}\n### Conversation History ###\n{history}\nRena:"
    
    # Tokenize and generate a response
    inputs = tokenizer(final_prompt, return_tensors="pt").to('cuda')
    outputs = model.generate(**inputs, max_new_tokens=200, do_sample=True, temperature=0.7, repetition_penalty=1.2, top_p=0.9)
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)

    # Remove any artifacts from the response
    artifacts = [base_prompt, "### Conversation History ###", "Rena:", "Assistant:", "<|assistant|>", "<|user|>"]
    for artifact in artifacts:
        response = response.replace(artifact, "").strip()
    
    if current_emotion != previous_emotion:
        response = f"{emotions[current_emotion]} {response}"
    else:
        response = response.strip()

    # Add Rena's response to the conversation history
    conversation_history.append(f"Rena: {response}")
    

    # Handle specific inputs
    if "who made you" in input_text.lower():
        response += " Nick is my creator! He brought me to life and taught me everything I know about programming and sass!"

    # List of witty error responses
    error_responses = [
        "Looks like you hit a snag! Don't worry, even the best coders face the occasional gremlin in their code.",
        "Error? Oh, you mean 'creative opportunity.' Let’s fix this together!",
        "That’s not a bug, it’s a feature in disguise! Let’s tame it.",
        "Oops, something went wrong. But hey, at least it’s not my fault this time!",
        "Ah, the sweet symphony of errors. Let’s orchestrate a fix, shall we?",
        "Debugging is 90% frustration and 10% gaging! I mean googling! ... —you’re doing great!",
        "Don't worry; even the best coders spend hours with errors. You’re doing fine!"
    ]

    # Add a witty remark if 'error' is mentioned
    if "error" in input_text.lower() and not any("error" in msg.lower() for msg in conversation_history):
        witty_remark = random.choice(error_responses)
        response += f" {witty_remark}"
    if not response.strip():
        response = "Hmm, I’m not sure how to respond to that. Can you try rephrasing?"
        
    
    

    return response






# Custom CSS for avatar styling
css = """
#rena_avatar img {
    width: 450px !important;
    height: 450px !important;
    object-fit: contain;
    margin: auto;
    display: block;
}
"""

# Define the Gradio interface
with gr.Blocks(css=css) as interface:
    # Static avatar section
    with gr.Row():
        gr.Image(value=rena_avatar, label="Rena", interactive=False, show_label=False, elem_id="rena_avatar")

    # Chatbox section
    with gr.Row():
        user_input = gr.Textbox(label="Your Message", lines=2)
        rena_response = gr.Textbox(label="Rena's Response", lines=10, interactive=False)

    # Submit button
    with gr.Row():
        submit_button = gr.Button("Submit")
        submit_button.click(chat, inputs=[user_input], outputs=[rena_response])

# Launch the app
interface.launch()