| |
| import os |
| import gradio as gr |
| from huggingface_hub import InferenceApi |
| from typing import List, Tuple |
|
|
| HF_TOKEN = os.environ.get("HUGGINGFACEHUB_API_TOKEN") |
| if not HF_TOKEN: |
| raise RuntimeError("Set HUGGINGFACEHUB_API_TOKEN in your Space secrets/settings.") |
|
|
| |
| TEXT_MODEL = os.environ.get("YAGA_TEXT_MODEL", "mistralai/Mistral-7B-v0.1") |
| IMAGE_CAPTION_MODEL = os.environ.get("YAGA_IMAGE_MODEL", "Salesforce/blip-image-captioning-large") |
| WHISPER_MODEL = os.environ.get("YAGA_WHISPER_MODEL", "openai/whisper-large-v2") |
|
|
| |
| text_api = InferenceApi(repo_id=TEXT_MODEL, token=HF_TOKEN) |
| image_api = InferenceApi(repo_id=IMAGE_CAPTION_MODEL, token=HF_TOKEN) |
| whisper_api = InferenceApi(repo_id=WHISPER_MODEL, token=HF_TOKEN) |
|
|
| SYSTEM_PROMPT = ( |
| "You are YAGA — an advanced, helpful, and safe AI assistant. " |
| "Answer concisely and clearly. If user provided an image or audio, " |
| "use the provided descriptions/transcripts as context." |
| ) |
|
|
| |
| |
| CONV_STORE = {} |
|
|
| def build_prompt(history: List[Tuple[str, str]], extra_context: str | None): |
| parts = [f"SYSTEM: {SYSTEM_PROMPT}"] |
| if extra_context: |
| parts.append(f"CONTEXT: {extra_context}") |
| for role, text in history: |
| parts.append(f"{role}: {text}") |
| parts.append("Assistant:") |
| return "\n".join(parts) |
|
|
| def get_session_history(session_id: str): |
| return CONV_STORE.setdefault(session_id, []) |
|
|
| def clear_chat(session_id: str): |
| CONV_STORE[session_id] = [] |
| return [] |
|
|
| def transcribe_audio(audio_bytes): |
| |
| try: |
| out = whisper_api(inputs=audio_bytes) |
| |
| if isinstance(out, dict) and "text" in out: |
| return out["text"] |
| if isinstance(out, str): |
| return out |
| |
| return str(out) |
| except Exception as e: |
| return f"[transcription_error: {e}]" |
|
|
| def caption_image(image_bytes): |
| try: |
| out = image_api(inputs=image_bytes) |
| |
| if isinstance(out, dict) and "generated_text" in out: |
| return out["generated_text"] |
| if isinstance(out, str): |
| return out |
| |
| return str(out) |
| except Exception as e: |
| return f"[caption_error: {e}]" |
|
|
| def generate_reply(prompt: str, max_tokens: int = 256, temperature: float = 0.7): |
| params = {"max_new_tokens": max_tokens, "temperature": temperature, "top_p":0.95} |
| |
| out = text_api(inputs=prompt, parameters=params) |
| if isinstance(out, dict): |
| |
| if "generated_text" in out: |
| return out["generated_text"] |
| |
| if "error" in out: |
| return f"[generation_error: {out['error']}]" |
| try: |
| return str(out) |
| except: |
| return "[generation_error: unexpected format]" |
| if isinstance(out, str): |
| return out |
| return str(out) |
|
|
| def respond(session_id, user_message, image_file, audio_file, max_tokens=256, temperature=0.7): |
| history = get_session_history(session_id) |
|
|
| extra_ctx_parts = [] |
| if image_file is not None: |
| img_bytes = image_file.read() |
| caption = caption_image(img_bytes) |
| extra_ctx_parts.append(f"Image caption: {caption}") |
| if audio_file is not None: |
| audio_bytes = audio_file.read() |
| transcript = transcribe_audio(audio_bytes) |
| extra_ctx_parts.append(f"Audio transcript: {transcript}") |
|
|
| extra_context = "\n".join(extra_ctx_parts) if extra_ctx_parts else None |
|
|
| |
| history.append(("User", user_message)) |
|
|
| prompt = build_prompt(history, extra_context) |
| reply = generate_reply(prompt, max_tokens=int(max_tokens), temperature=float(temperature)) |
|
|
| history.append(("Assistant", reply)) |
| |
| CONV_STORE[session_id] = history[-12*2:] |
| |
| chat_pairs = [(r[0], r[1]) for r in CONV_STORE[session_id]] |
| return chat_pairs, "" |
|
|
| with gr.Blocks(title="YAGA — HF Space Prototype") as demo: |
| gr.Markdown("## YAGA — Multi-turn chat (Hugging Face Space)\nText, image and audio inputs supported.\n\n**Important:** Add your Hugging Face token to Space secrets as `HUGGINGFACEHUB_API_TOKEN`.") |
| with gr.Row(): |
| with gr.Column(scale=3): |
| chatbot = gr.Chatbot() |
| msg = gr.Textbox(placeholder="Type your message here...", label="Message") |
| with gr.Row(): |
| img = gr.Image(type="bytes", label="Optional image") |
| aud = gr.Audio(source="upload", type="file", label="Optional audio") |
| with gr.Row(): |
| max_t = gr.Slider(32, 1024, value=256, step=32, label="Max new tokens") |
| temp = gr.Slider(0.0, 1.0, value=0.7, step=0.05, label="Temperature") |
| with gr.Row(): |
| send = gr.Button("Send") |
| clear = gr.Button("Clear chat") |
| with gr.Column(scale=1): |
| gr.Markdown("### Session Info") |
| session_id_display = gr.Textbox(value="", interactive=False, label="Session ID (auto)") |
| gr.Markdown("### Hints") |
| gr.Markdown("- Upload an image to give the model visual context (it will be captioned).\n- Upload audio to transcribe via Whisper.\n- Replace `TEXT_MODEL` env var with your model repo for a custom model.") |
| |
| def get_sid(): |
| import uuid |
| return str(uuid.uuid4()) |
| sid = get_sid() |
| session_id_display.value = sid |
|
|
| def on_send(message, image_file, audio_file, max_tokens, temperature): |
| pairs, _ = respond(sid, message, image_file, audio_file, max_tokens, temperature) |
| |
| chat_display = [] |
| it = iter(pairs) |
| |
| |
| for role, text in pairs: |
| |
| chat_display.append((role, text)) |
| |
| |
| last_user = pairs[-2][1] if len(pairs) >= 2 else "" |
| last_assistant = pairs[-1][1] |
| return gr.update(value=chat_display), "" |
|
|
| send.click(on_send, [msg, img, aud, max_t, temp], [chatbot, msg]) |
| def on_clear(): |
| clear_chat(sid) |
| return [], "" |
| clear.click(on_clear, [], [chatbot, msg]) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|