WillHeld's picture
Update app.py
515ea2f verified
import spaces
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
import gradio as gr
from threading import Thread
import os
import json
import uuid
from datasets import Dataset
from huggingface_hub import HfApi, login
import time
# Install required packages if not present
from gradio_modal import Modal
import huggingface_hub
import datasets
# Model setup
checkpoint = "WillHeld/soft-raccoon"
device = "cuda"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForCausalLM.from_pretrained(checkpoint).to(device)
# Constants for dataset
DATASET_REPO = "WillHeld/model-feedback" # Replace with your username
DATASET_PATH = "./feedback_data" # Local path to store feedback
DATASET_FILENAME = "feedback.jsonl" # Filename for feedback data
# Ensure feedback directory exists
os.makedirs(DATASET_PATH, exist_ok=True)
# Feedback storage functions
def save_feedback_locally(conversation, satisfaction, feedback_text):
"""Save feedback to a local JSONL file"""
# Create a unique ID for this feedback entry
feedback_id = str(uuid.uuid4())
# Create a timestamp
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# Prepare the feedback data
feedback_data = {
"id": feedback_id,
"timestamp": timestamp,
"conversation": conversation,
"satisfaction": satisfaction,
"feedback": feedback_text
}
# Save to local file
feedback_file = os.path.join(DATASET_PATH, DATASET_FILENAME)
with open(feedback_file, "a") as f:
f.write(json.dumps(feedback_data) + "\n")
return feedback_id
def push_feedback_to_hub(hf_token=None):
"""Push the local feedback data to HuggingFace as a dataset"""
# Check if we have a token
if hf_token is None:
# Try to get token from environment variable
hf_token = os.environ.get("HF_TOKEN")
if hf_token is None:
print("No HuggingFace token provided. Cannot push to Hub.")
return False
try:
# Login to HuggingFace
login(token=hf_token)
# Check if we have data to push
feedback_file = os.path.join(DATASET_PATH, DATASET_FILENAME)
if not os.path.exists(feedback_file):
print("No feedback data to push.")
return False
# Load data from the JSONL file
with open(feedback_file, "r") as f:
feedback_data = [json.loads(line) for line in f]
# Create a dataset from the feedback data
dataset = Dataset.from_list(feedback_data)
# Push to Hub
dataset.push_to_hub(
DATASET_REPO,
private=True # Set to False if you want the dataset to be public
)
print(f"Feedback data pushed to {DATASET_REPO} successfully.")
return True
except Exception as e:
print(f"Error pushing feedback data to Hub: {e}")
return False
# Modified predict function to update conversation state
@spaces.GPU(duration=120)
def predict(message, history, temperature, top_p):
# Update history with user message
history.append({"role": "user", "content": message})
input_text = tokenizer.apply_chat_template(history, tokenize=False, add_generation_prompt=True)
inputs = tokenizer.encode(input_text, return_tensors="pt").to(device)
# Create a streamer
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
# Set up generation parameters
generation_kwargs = {
"input_ids": inputs,
"max_new_tokens": 1024,
"temperature": float(temperature),
"top_p": float(top_p),
"do_sample": True,
"streamer": streamer,
}
# Run generation in a separate thread
thread = Thread(target=model.generate, kwargs=generation_kwargs)
thread.start()
# Yield from the streamer as tokens are generated
partial_text = ""
for new_text in streamer:
partial_text += new_text
yield partial_text
# After full generation, update state with assistant's response
history.append({"role": "assistant", "content": partial_text})
return partial_text
# Function to handle the research feedback submission
def submit_research_feedback(conversation_state, satisfaction, feedback_text):
"""Save user feedback both locally and to HuggingFace Hub"""
# Save locally first
feedback_id = save_feedback_locally(conversation_state, satisfaction, feedback_text)
# Get token from environment variable
env_token = os.environ.get("HF_TOKEN")
# Use environment token
push_success = push_feedback_to_hub(env_token)
if push_success:
status_msg = "Thank you for your valuable feedback! Your insights have been saved to the dataset."
else:
status_msg = "Thank you for your feedback! It has been saved locally, but couldn't be pushed to the dataset. Please check server logs."
return status_msg
# Create the Gradio blocks interface
with gr.Blocks() as demo:
# State to track conversation history
conversation_state = gr.State([])
with gr.Row():
with gr.Column(scale=3):
# Custom chat function wrapper to update state
def chat_with_state(message, history, state, temperature, top_p):
for partial_response in predict(message, history, temperature, top_p):
# Update our state with each yield
state = history.copy()
yield partial_response, state
state = history.copy()
print(state)
return partial_response, state
# Create ChatInterface
chatbot = gr.ChatInterface(
chat_with_state,
additional_inputs=[
conversation_state,
gr.Slider(0.1, 2.0, value=0.7, step=0.1, label="Temperature"),
gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-P")
],
additional_outputs=[conversation_state],
type="messages"
)
with gr.Column(scale=1):
report_button = gr.Button("Share Feedback", variant="primary")
# Create the modal with feedback form components
with Modal(visible=False) as feedback_modal:
with gr.Column():
gr.Markdown("## Research Preview Feedback")
gr.Markdown("Thank you for testing our research model. Your feedback (positive or negative) helps us improve!")
satisfaction = gr.Radio(
["Very satisfied", "Satisfied", "Neutral", "Unsatisfied", "Very unsatisfied"],
label="How would you rate your experience with this research model?",
value="Neutral"
)
feedback_text = gr.Textbox(
lines=5,
label="Share your observations (strengths, weaknesses, suggestions):",
placeholder="We welcome both positive feedback and constructive criticism to help improve this research prototype..."
)
submit_button = gr.Button("Submit Research Feedback", variant="primary")
response_text = gr.Textbox(label="Status", interactive=False)
# Connect the "Share Feedback" button to show the modal
report_button.click(
lambda: Modal(visible=True),
None,
feedback_modal
)
# Connect the submit button to the submit_research_feedback function with the current conversation state
submit_button.click(
submit_research_feedback,
inputs=[conversation_state, satisfaction, feedback_text],
outputs=response_text
)
# Launch the demo
demo.launch()