Spaces:
Running
Running
import time | |
import gradio as gr | |
import pandas as pd | |
import openvino_genai | |
from huggingface_hub import snapshot_download | |
from threading import Lock, Event | |
import os | |
import numpy as np | |
import requests | |
from PIL import Image | |
from io import BytesIO | |
import cpuinfo | |
import openvino as ov | |
import librosa | |
from googleapiclient.discovery import build | |
import gc | |
from PyPDF2 import PdfReader | |
from docx import Document | |
import textwrap | |
from queue import Queue, Empty | |
from concurrent.futures import ThreadPoolExecutor | |
from typing import Generator | |
GOOGLE_API_KEY = "AIzaSyAo-1iW5MEZbc53DlEldtnUnDaYuTHUDH4" | |
GOOGLE_CSE_ID = "3027bedf3c88a4efb" | |
DEFAULT_MAX_TOKENS = 4096 | |
DEFAULT_NUM_IMAGES = 1 | |
MAX_HISTORY_TURNS = 3 | |
MAX_TOKENS_LIMIT = 4096 | |
class UnifiedAISystem: | |
def __init__(self): | |
self.pipe_lock = Lock() | |
self.current_df = None | |
self.mistral_pipe = None | |
self.internvl_pipe = None | |
self.whisper_pipe = None | |
self.current_document_text = None | |
self.generation_executor = ThreadPoolExecutor(max_workers=3) | |
self.initialize_models() | |
def initialize_models(self): | |
"""Initialize all required models""" | |
if not os.path.exists("mistral-ov"): | |
snapshot_download(repo_id="OpenVINO/mistral-7b-instruct-v0.1-int8-ov", local_dir="mistral-ov") | |
if not os.path.exists("internvl-ov"): | |
snapshot_download(repo_id="OpenVINO/InternVL2-1B-int8-ov", local_dir="internvl-ov") | |
if not os.path.exists("whisper-ov-model"): | |
snapshot_download(repo_id="OpenVINO/whisper-tiny-fp16-ov", local_dir="whisper-ov-model") | |
cpu_features = cpuinfo.get_cpu_info()['flags'] | |
config_options = {} | |
if 'avx512' in cpu_features: | |
config_options["ENFORCE_BF16"] = "YES" | |
elif 'avx2' in cpu_features: | |
config_options["INFERENCE_PRECISION_HINT"] = "f32" | |
# Initialize Mistral model | |
self.mistral_pipe = openvino_genai.LLMPipeline( | |
"mistral-ov", | |
device="CPU", | |
config={"PERFORMANCE_HINT": "THROUGHPUT", **config_options} | |
) | |
self.whisper_pipe = openvino_genai.WhisperPipeline("whisper-ov-model", device="CPU") | |
def load_data(self, file_path): | |
"""Load student data from file""" | |
try: | |
file_ext = os.path.splitext(file_path)[1].lower() | |
if file_ext == '.csv': | |
self.current_df = pd.read_csv(file_path) | |
elif file_ext in ['.xlsx', '.xls']: | |
self.current_df = pd.read_excel(file_path) | |
else: | |
return False, "β Unsupported file format. Please upload a .csv or .xlsx file." | |
return True, f"β Loaded {len(self.current_df)} records from {os.path.basename(file_path)}" | |
except Exception as e: | |
return False, f"β Error loading file: {str(e)}" | |
def extract_text_from_document(self, file_path): | |
"""Extract text from PDF or DOCX documents""" | |
text = "" | |
try: | |
file_ext = os.path.splitext(file_path)[1].lower() | |
if file_ext == '.pdf': | |
with open(file_path, 'rb') as file: | |
pdf_reader = PdfReader(file) | |
for page in pdf_reader.pages: | |
text += page.extract_text() + "\n" | |
elif file_ext == '.docx': | |
doc = Document(file_path) | |
for para in doc.paragraphs: | |
text += para.text + "\n" | |
else: | |
return False, "β Unsupported document format. Please upload PDF or DOCX." | |
# Clean and format text | |
text = text.replace('\x0c', '') | |
text = textwrap.dedent(text) | |
self.current_document_text = text | |
return True, f"β Extracted text from {os.path.basename(file_path)}" | |
except Exception as e: | |
return False, f"β Error processing document: {str(e)}" | |
def generate_text_stream(self, prompt: str, max_tokens: int) -> Generator[str, None, None]: | |
"""Unified text generation with queued token streaming""" | |
start_time = time.time() | |
response_queue = Queue() | |
completion_event = Event() | |
error = [None] | |
optimized_config = openvino_genai.GenerationConfig( | |
max_new_tokens=max_tokens, | |
temperature=0.3, | |
top_p=0.9, | |
streaming=True, | |
streaming_interval=5 | |
) | |
def callback(tokens): | |
response_queue.put("".join(tokens)) | |
return openvino_genai.StreamingStatus.RUNNING | |
def generate(): | |
try: | |
with self.pipe_lock: | |
self.mistral_pipe.generate(prompt, optimized_config, callback) | |
except Exception as e: | |
error[0] = str(e) | |
finally: | |
completion_event.set() | |
self.generation_executor.submit(generate) | |
accumulated = [] | |
token_count = 0 | |
last_gc = time.time() | |
while not completion_event.is_set() or not response_queue.empty(): | |
if error[0]: | |
yield f"β Error: {error[0]}" | |
print(f"Stream generation time: {time.time() - start_time:.2f} seconds") | |
return | |
try: | |
token_batch = response_queue.get(timeout=0.1) | |
accumulated.append(token_batch) | |
token_count += len(token_batch) | |
yield "".join(accumulated) | |
if time.time() - last_gc > 2.0: | |
gc.collect() | |
last_gc = time.time() | |
except Empty: | |
continue | |
print(f"Generated {token_count} tokens in {time.time() - start_time:.2f} seconds " | |
f"({token_count/(time.time() - start_time):.2f} tokens/sec)") | |
yield "".join(accumulated) | |
def analyze_student_data(self, query, max_tokens=4098): | |
"""Analyze student data using AI with streaming""" | |
if not query or not query.strip(): | |
yield "β οΈ Please enter a valid question" | |
return | |
if self.current_df is None: | |
yield "β οΈ Please upload and load a student data file first" | |
return | |
data_summary = self._prepare_data_summary(self.current_df) | |
prompt = f"""You are an expert education analyst. Analyze the following student performance data: | |
{data_summary} | |
Question: {query} | |
Please include: | |
1. Direct answer to the question | |
2. Relevant statistics | |
3. Key insights | |
4. Actionable recommendations | |
Format the output with clear headings""" | |
yield from self.generate_text_stream(prompt, max_tokens) | |
def _prepare_data_summary(self, df): | |
"""Summarize the uploaded data""" | |
summary = f"Student performance data with {len(df)} rows and {len(df.columns)} columns.\n" | |
summary += "Columns: " + ", ".join(df.columns) + "\n" | |
summary += "First 3 rows:\n" + df.head(3).to_string(index=False) | |
return summary | |
def analyze_image(self, image, url, prompt): | |
"""Analyze image with InternVL model (synchronous, no streaming)""" | |
try: | |
if image is not None: | |
image_source = image | |
elif url and url.startswith(("http://", "https://")): | |
response = requests.get(url) | |
image_source = Image.open(BytesIO(response.content)).convert("RGB") | |
else: | |
return "β οΈ Please upload an image or enter a valid URL" | |
image_data = np.array(image_source.getdata()).reshape( | |
1, image_source.size[1], image_source.size[0], 3 | |
).astype(np.byte) | |
image_tensor = ov.Tensor(image_data) | |
if self.internvl_pipe is None: | |
self.internvl_pipe = openvino_genai.VLMPipeline("internvl-ov", device="CPU") | |
with self.pipe_lock: | |
self.internvl_pipe.start_chat() | |
output = self.internvl_pipe.generate(prompt, image=image_tensor, max_new_tokens=4090) | |
self.internvl_pipe.finish_chat() | |
return output | |
except Exception as e: | |
return f"β Error: {str(e)}" | |
def process_audio(self, data, sr): | |
"""Process audio data for speech recognition""" | |
try: | |
if data.ndim > 1: | |
data = np.mean(data, axis=1) | |
else: | |
data = data | |
data = data.astype(np.float32) | |
max_val = np.max(np.abs(data)) + 1e-7 | |
data /= max_val | |
# Simple noise reduction | |
data = np.clip(data, -0.5, 0.5) | |
# Trim silence | |
energy = np.abs(data) | |
threshold = np.percentile(energy, 25) | |
mask = energy > threshold | |
indices = np.where(mask)[0] | |
if len(indices) > 0: | |
start = max(0, indices[0] - 1000) | |
end = min(len(data), indices[-1] + 1000) | |
data = data[start:end] | |
if sr != 16000: | |
new_length = int(len(data) * 16000 / sr) | |
data = np.interp( | |
np.linspace(0, len(data)-1, new_length), | |
np.arange(len(data)), | |
data | |
) | |
sr = 16000 | |
return data | |
except Exception as e: | |
print(f"Audio processing error: {e}") | |
return np.array([], dtype=np.float32) | |
def transcribe(self, audio): | |
"""Transcribe audio using Whisper model with improved error handling""" | |
if audio is None: | |
return "" | |
sr, data = audio | |
if len(data)/sr < 0.5: | |
return "" | |
try: | |
processed = self.process_audio(data, sr) | |
if len(processed) < 8000: | |
return "" | |
result = self.whisper_pipe.generate(processed) | |
return result | |
except Exception as e: | |
print(f"Transcription error: {e}") | |
return "β Transcription failed - please try again" | |
def generate_lesson_plan(self, topic, duration, additional_instructions="", max_tokens=4096): | |
"""Generate a lesson plan based on document content""" | |
if not topic: | |
yield "β οΈ Please enter a lesson topic" | |
return | |
if not self.current_document_text: | |
yield "β οΈ Please upload and process a document first" | |
return | |
prompt = f"""As an expert educator, create a focused lesson plan using the provided content. | |
**Core Requirements:** | |
1. TOPIC: {topic} | |
2. TOTAL DURATION: {duration} periods | |
3. ADDITIONAL INSTRUCTIONS: {additional_instructions or 'None'} | |
**Content Summary:** | |
{self.current_document_text[:2500]}... [truncated] | |
**Output Structure:** | |
1. PERIOD ALLOCATION (Break topic into {duration} logical segments): | |
- Period 1: [Subtopic 1] | |
- Period 2: [Subtopic 2] | |
... | |
2. LEARNING OBJECTIVES (Max 3 bullet points) | |
3. TEACHING ACTIVITIES (One engaging method per period) | |
4. RESOURCES (Key materials from document) | |
5. ASSESSMENT (Simple checks for understanding) | |
6. PAGE REFERENCES (Specific source pages) | |
**Key Rules:** | |
- Strictly divide content into exactly {duration} periods | |
- Prioritize document content over creativity | |
- Keep objectives measurable | |
- Use only document resources | |
- Make page references specific""" | |
yield from self.generate_text_stream(prompt, max_tokens) | |
def fetch_images(self, query: str, num: int = DEFAULT_NUM_IMAGES) -> list: | |
"""Fetch unique images by requesting different result pages""" | |
if num <= 0: | |
return [] | |
try: | |
service = build("customsearch", "v1", developerKey=GOOGLE_API_KEY) | |
image_links = [] | |
seen_urls = set() | |
for start_index in range(1, num * 2, 2): | |
if len(image_links) >= num: | |
break | |
res = service.cse().list( | |
q=query, | |
cx=GOOGLE_CSE_ID, | |
searchType="image", | |
num=1, | |
start=start_index | |
).execute() | |
if "items" in res and res["items"]: | |
item = res["items"][0] | |
# Skip duplicates | |
if item["link"] not in seen_urls: | |
image_links.append(item["link"]) | |
seen_urls.add(item["link"]) | |
return image_links[:num] | |
except Exception as e: | |
print(f"Error in image fetching: {e}") | |
return [] | |
ai_system = UnifiedAISystem() | |
css = """ | |
:root { | |
--bg: #0D0D0D; | |
--surface: #1F1F1F; | |
--primary: #BB86FC; | |
--secondary: #03DAC6; | |
--accent: #CF6679; | |
--success: #4CAF50; | |
--warning: #FFB300; | |
--text: #FFFFFF; | |
--subtext: #B0B0B0; | |
--divider: #333333; | |
} | |
body, .gradio-container { background: var(--bg); color: var(--text); } | |
.user-msg, | |
.bot-msg, | |
.upload-box, | |
#question-input, | |
.mode-checkbox, | |
.system-info, | |
.lesson-plan { background: var(--surface); border-radius: 8px; color: var(--text); } | |
.user-msg, | |
.bot-msg { padding: 12px 16px; margin: 8px 0; line-height:1.5; border-left:4px solid var(--primary); box-shadow:0 2px 6px rgba(0,0,0,0.5); } | |
.bot-msg { border-color: var(--secondary); } | |
.upload-box { padding:16px; margin-bottom:16px; border:1px solid var(--divider); } | |
#question-input, | |
.mode-checkbox { padding:12px; border:1px solid var(--divider); } | |
.slider-container { margin:20px 0; padding:15px; border-radius:10px; background:var(--secondary); } | |
.system-info { padding:15px; margin:15px 0; border-left:4px solid var(--primary); } | |
.chat-image { max-height:100px; margin:4px; border-radius:8px; box-shadow:0 2px 6px rgba(0,0,0,0.5); cursor:pointer; transition:transform .2s; } | |
.chat-image:hover { transform:scale(1.05); box-shadow:0 4px 10px rgba(0,0,0,0.7); } | |
.modal { position:fixed; inset:0; background:rgba(0,0,0,0.9); display:none; cursor:zoom-out; } | |
.modal-content { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); max-width:90%; max-height:90%; padding:10px; border-radius:12px; background:var(--surface); } | |
.modal-img { max-width:100%; max-height:100%; border-radius:8px; } | |
.typing-indicator { display:inline-block; position:relative; width:40px; height:20px; } | |
.typing-dot { width:6px; height:6px; border-radius:50%; background:var(--text); position:absolute; animation:typing 1.4s infinite ease-in-out; } | |
.typing-dot:nth-child(1){left:0;} | |
.typing-dot:nth-child(2){left:12px;animation-delay:.2s} | |
.typing-dot:nth-child(3){left:24px;animation-delay:.4s} | |
@keyframes typing{0%,60%,100%{transform:translateY(0)}30%{transform:translateY(-5px)}} | |
.lesson-title { font-size:1.2em; font-weight:bold; color:var(--primary); margin-bottom:8px; } | |
.page-ref { display:inline-block; padding:3px 8px; margin:3px; border-radius:4px; background:var(--primary); color:var(--text); font-size:.9em; } | |
/* Scrollbar */ | |
.chatbot::-webkit-scrollbar{width:8px} | |
.chatbot::-webkit-scrollbar-track{background:var(--surface);border-radius:4px} | |
.chatbot::-webkit-scrollbar-thumb{background:var(--primary);border-radius:4px} | |
.chatbot::-webkit-scrollbar-thumb:hover{background:var(--secondary)} | |
""" | |
with gr.Blocks(css=css, title="Unified EDU Assistant") as demo: | |
gr.Markdown("# π€ Unified EDU Assistant by ALPHA") | |
gr.HTML(""" | |
<div class="system-info"> | |
<strong>Multi-Modal AI Assistant</strong> | |
<ul> | |
<li>Text & Voice Chat with Mistral-7B</li> | |
<li>Image Understanding with InternVL</li> | |
<li>Student Data Analysis</li> | |
<li>Visual Search with Google Images</li> | |
<li>Lesson Planning from Documents</li> | |
</ul> | |
</div> | |
""") | |
modal_html = """ | |
<div class="modal" id="imageModal" onclick="this.style.display='none'"> | |
<div class="modal-content"> | |
<img class="modal-img" id="expandedImg"> | |
</div> | |
</div> | |
<script> | |
function showImage(url) { | |
document.getElementById('expandedImg').src = url; | |
document.getElementById('imageModal').style.display = 'block'; | |
} | |
</script> | |
""" | |
gr.HTML(modal_html) | |
chat_state = gr.State([]) | |
with gr.Column(scale=2, elem_classes="chat-container"): | |
chatbot = gr.Chatbot(label="Conversation", height=500, bubble_full_width=False, | |
avatar_images=("user.png", "bot.png"), show_label=False) | |
with gr.Row(): | |
chat_mode = gr.Checkbox(label="π¬ General Chat", value=True, elem_classes="mode-checkbox") | |
student_mode = gr.Checkbox(label="π Student Analytics", value=False, elem_classes="mode-checkbox") | |
image_mode = gr.Checkbox(label="πΌοΈ Image Analysis", value=False, elem_classes="mode-checkbox") | |
lesson_mode = gr.Checkbox(label="π Lesson Planning", value=False, elem_classes="mode-checkbox") | |
with gr.Column() as chat_inputs: | |
include_images = gr.Checkbox(label="Include Visuals", value=True) | |
user_input = gr.Textbox( | |
placeholder="Type your question here...", | |
label="Your Question", | |
container=False, | |
elem_id="question-input" | |
) | |
with gr.Row(): | |
max_tokens = gr.Slider( | |
minimum=10, | |
maximum=7910, | |
value=2048, | |
step=100, | |
label="Response Length (Tokens)" | |
) | |
num_images = gr.Slider( | |
minimum=0, | |
maximum=5, | |
value=1, | |
step=1, | |
label="Number of Images", | |
visible=True | |
) | |
with gr.Column(visible=False) as student_inputs: | |
file_upload = gr.File(label="CSV/Excel File", file_types=[".csv", ".xlsx"], type="filepath") | |
student_question = gr.Textbox( | |
placeholder="Ask questions about student data...", | |
label="Your Question", | |
elem_id="question-input" | |
) | |
student_status = gr.Markdown("No file loaded") | |
with gr.Column(visible=False) as image_inputs: | |
image_upload = gr.Image(type="pil", label="Upload Image") | |
image_url = gr.Textbox( | |
label="OR Enter Image URL", | |
placeholder="https://example.com/image.jpg", | |
elem_id="question-input" | |
) | |
image_question = gr.Textbox( | |
placeholder="Ask questions about the image...", | |
label="Your Question", | |
elem_id="question-input" | |
) | |
with gr.Column(visible=False) as lesson_inputs: | |
gr.Markdown("### π Lesson Planning") | |
doc_upload = gr.File( | |
label="Upload Curriculum Document (PDF/DOCX)", | |
file_types=[".pdf", ".docx"], | |
type="filepath" | |
) | |
doc_status = gr.Markdown("No document uploaded") | |
with gr.Row(): | |
topic_input = gr.Textbox( | |
label="Lesson Topic", | |
placeholder="Enter the main topic for the lesson plan" | |
) | |
duration_input = gr.Number( | |
label="Total Periods", | |
value=5, | |
minimum=1, | |
maximum=20, | |
step=1 | |
) | |
additional_instructions = gr.Textbox( | |
label="Additional Requirements (optional)", | |
placeholder="Specific teaching methods, resources, or special considerations..." | |
) | |
generate_btn = gr.Button("Generate Lesson Plan", variant="primary") | |
with gr.Row(): | |
submit_btn = gr.Button("Send", variant="primary") | |
mic_btn = gr.Button("Transcribe Voice", variant="secondary") | |
mic = gr.Audio(sources=["microphone"], type="numpy", label="Voice Input") | |
def toggle_modes(chat, student, image, lesson): | |
return [ | |
gr.update(visible=chat), | |
gr.update(visible=student), | |
gr.update(visible=image), | |
gr.update(visible=lesson) | |
] | |
def load_student_file(file_path): | |
success, message = ai_system.load_data(file_path) | |
return message | |
def process_document(file_path): | |
if not file_path: | |
return "β οΈ Please select a document first" | |
success, message = ai_system.extract_text_from_document(file_path) | |
return message | |
def render_history(history): | |
"""Render chat history with images and proper formatting""" | |
rendered = [] | |
for user_msg, bot_msg, image_links in history: | |
user_html = f"<div class='user-msg'>{user_msg}</div>" | |
bot_text = str(bot_msg) | |
if "Lesson Plan:" in bot_text: | |
bot_html = f"<div class='lesson-plan'>{bot_text}</div>" | |
else: | |
bot_html = f"<div class='bot-msg'>{bot_text}</div>" | |
# Add images if available | |
if image_links: | |
images_html = "".join( | |
f"<img src='{url}' class='chat-image' onclick='showImage(\"{url}\")' />" | |
for url in image_links | |
) | |
bot_html += f"<br><br><b>πΈ Related Visuals:</b><br><div style='display: flex; flex-wrap: wrap;'>{images_html}</div>" | |
rendered.append((user_html, bot_html)) | |
return rendered | |
def respond(message, history, chat, student, image, lesson, | |
tokens, student_q, image_q, image_upload, image_url, | |
include_visuals, num_imgs, topic, duration, additional): | |
""" | |
1. Use actual_message (depending on mode) instead of raw `message`. | |
2. Convert any nonβstring Bot response (like VLMDecodedResults) to str(). | |
3. Disable the input box during streaming, then re-enable it at the end. | |
""" | |
updated_history = list(history) | |
if student: | |
actual_message = student_q | |
elif image: | |
actual_message = image_q | |
elif lesson: | |
actual_message = f"Generate lesson plan for: {topic} ({duration} periods)" | |
if additional: | |
actual_message += f"\nAdditional: {additional}" | |
else: | |
actual_message = message | |
typing_html = "<div class='typing-indicator'><div class='typing-dot'></div><div class='typing-dot'></div><div class='typing-dot'></div></div>" | |
updated_history.append((actual_message, typing_html, [])) | |
yield render_history(updated_history), gr.update(value="", interactive=False), updated_history | |
full_response = "" | |
images = [] | |
try: | |
if chat: | |
for chunk in ai_system.generate_text_stream(actual_message, tokens): | |
full_response = chunk | |
updated_history[-1] = (actual_message, full_response, []) | |
yield render_history(updated_history), gr.update(value="", interactive=False), updated_history | |
if include_visuals: | |
images = ai_system.fetch_images(actual_message, num_imgs) | |
elif student: | |
if ai_system.current_df is None: | |
full_response = "β οΈ Please upload a student data file first" | |
else: | |
for chunk in ai_system.analyze_student_data(student_q, tokens): | |
full_response = chunk | |
updated_history[-1] = (actual_message, full_response, []) | |
yield render_history(updated_history), gr.update(value="", interactive=False), updated_history | |
elif image: | |
if (not image_upload) and (not image_url): | |
full_response = "β οΈ Please upload an image or enter a URL" | |
else: | |
result_obj = ai_system.analyze_image(image_upload, image_url, image_q) | |
full_response = str(result_obj) | |
elif lesson: | |
if not topic: | |
full_response = "β οΈ Please enter a lesson topic" | |
else: | |
duration = int(duration) if duration else 5 | |
for chunk in ai_system.generate_lesson_plan(topic, duration, additional, tokens): | |
full_response = chunk | |
updated_history[-1] = (actual_message, full_response, []) | |
yield render_history(updated_history), gr.update(value="", interactive=False), updated_history | |
updated_history[-1] = (actual_message, full_response, images) | |
if len(updated_history) > MAX_HISTORY_TURNS: | |
updated_history = updated_history[-MAX_HISTORY_TURNS:] | |
except Exception as e: | |
error_msg = f"β Error: {str(e)}" | |
updated_history[-1] = (actual_message, error_msg, []) | |
yield render_history(updated_history), gr.update(value="", interactive=True), updated_history | |
# Voice transcription | |
def transcribe_audio(audio): | |
return ai_system.transcribe(audio) | |
chat_mode.change(fn=toggle_modes, inputs=[chat_mode, student_mode, image_mode, lesson_mode], | |
outputs=[chat_inputs, student_inputs, image_inputs, lesson_inputs]) | |
student_mode.change(fn=toggle_modes, inputs=[chat_mode, student_mode, image_mode, lesson_mode], | |
outputs=[chat_inputs, student_inputs, image_inputs, lesson_inputs]) | |
image_mode.change(fn=toggle_modes, inputs=[chat_mode, student_mode, image_mode, lesson_mode], | |
outputs=[chat_inputs, student_inputs, image_inputs, lesson_inputs]) | |
lesson_mode.change(fn=toggle_modes, inputs=[chat_mode, student_mode, image_mode, lesson_mode], | |
outputs=[chat_inputs, student_inputs, image_inputs, lesson_inputs]) | |
# File upload handler | |
file_upload.change(fn=load_student_file, inputs=file_upload, outputs=student_status) | |
# Document upload handler | |
doc_upload.change(fn=process_document, inputs=doc_upload, outputs=doc_status) | |
mic_btn.click(fn=transcribe_audio, inputs=mic, outputs=user_input) | |
# Submit handler | |
submit_btn.click( | |
fn=respond, | |
inputs=[ | |
user_input, chat_state, chat_mode, student_mode, image_mode, lesson_mode, | |
max_tokens, student_question, image_question, image_upload, image_url, | |
include_images, num_images, | |
topic_input, duration_input, additional_instructions | |
], | |
outputs=[chatbot, user_input, chat_state] | |
) | |
generate_btn.click( | |
fn=respond, | |
inputs=[ | |
gr.Textbox(value="Generate lesson plan", visible=False), | |
chat_state, | |
chat_mode, student_mode, image_mode, lesson_mode, | |
max_tokens, | |
gr.Textbox(visible=False), | |
gr.Textbox(visible=False), | |
gr.Image(visible=False), | |
gr.Textbox(visible=False), | |
gr.Checkbox(visible=False), | |
gr.Slider(visible=False), | |
topic_input, | |
duration_input, | |
additional_instructions | |
], | |
outputs=[chatbot, user_input, chat_state] | |
) | |
if __name__ == "__main__": | |
demo.launch(share=True, debug=True) |