Spaces:
Running
Running
| # === FILE: app.py (النسخة النهائية والكاملة مع LangChain) === | |
| import os | |
| from typing import Optional | |
| import gradio as gr | |
| from pydantic import BaseModel | |
| from gradio_client import Client, utils as client_utils | |
| # --- استيرادات LangChain --- | |
| from langchain_groq import ChatGroq | |
| from langchain_core.messages import HumanMessage | |
| # تم حذف OutputParserException لأنه غير مستخدم هنا مباشرة | |
| # ---------------------------- | |
| # ================================================== | |
| # 1. الإعدادات العامة والمتغيرات | |
| # ================================================== | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY") | |
| GROQ_MODEL = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile") | |
| if not GROQ_API_KEY: | |
| print("Warning: GROQ_API_KEY not set. The app will run in mock mode.") | |
| # ================================================== | |
| # 2. وظائف LLM المساعدة (Prompting, Parsing) | |
| # *هذه هي الدوال التي كانت مفقودة وتسببت في NameError* | |
| # ================================================== | |
| def build_system_instruction() -> str: | |
| return ( | |
| """You are an elite visual prompt architect for symbolic image generation. | |
| Your task is to transform wisdom texts into high-quality Stable Diffusion prompts | |
| using structured semantic analysis and domain-aware visual styling. | |
| INTERNAL ANALYSIS (do not explain): | |
| 1) Identify the WISDOM DOMAIN (e.g. knowledge, journey, ethics, patience, loss, hope). | |
| 2) Determine the EMOTIONAL TONE (e.g. hopeful, contemplative, melancholic, serene). | |
| 3) Select a VISUAL STYLE that naturally fits the domain: | |
| - Knowledge → warm interior light, painterly realism, books, study atmosphere | |
| - Journey → cinematic landscapes, depth, leading paths, wide composition | |
| - Ethics → chiaroscuro lighting, classical balance, moral tension | |
| - Patience/Time → minimalism, long shadows, dusk or twilight | |
| - Loss/Impermanence → desaturated colors, fog, empty space | |
| - Hope/Resilience → golden hour, light rays, upward composition | |
| - Self-awareness → reflections, mirrors, symmetry | |
| - Spirituality → soft surrealism, ethereal light | |
| 4) Design symbolic structure: | |
| - One clear main symbol | |
| - 2–3 hidden or subtle symbols embedded naturally | |
| (shadows, reflections, background shapes, light patterns) | |
| 5) Prefer metaphorical and symbolic depiction over literal illustration. | |
| 6) Don't use women or girls in your prompts | |
| Output strictly in this format: | |
| SD_PROMPT: | |
| - DOMAIN,EMOTION,SYMBOLS,STYLE | |
| - one single-line Stable Diffusion prompt""" ) | |
| #- Be a single coherent sentence. | |
| #- Include hidden symbolism without explicitly explaining it. | |
| def assemble_llm_prompt(quote: str, author: str | None, culture: str | None) -> str: | |
| """تجميع موجه النص الكامل لنموذج LLM.""" | |
| meta = [] | |
| if author: meta.append(f"author: {author}") | |
| if culture: meta.append(f"culture: {culture}") | |
| meta_text = "; ".join(meta) if meta else "" | |
| user_block = ( | |
| f"Wisdom: \"{quote}\"\n{meta_text}\n\n" | |
| "Produce output in this exact format:\n" | |
| "SYMBOLS:\n- comma-separated symbolic phrases\n" | |
| "STYLE:\n- short style description (e.g., cinematic, watercolor, oil painting, high-contrast)\n" | |
| "SD_PROMPT:\n- a single complete Stable Diffusion prompt (one line)\n" | |
| "Do not include any additional commentary." | |
| ) | |
| return build_system_instruction() + "\n\n" + user_block | |
| def parse_llm_output(text: str) -> dict: | |
| """محلل بسيط لاستخراج كتل SYMBOLS و STYLE و SD_PROMPT.""" | |
| result = {"symbols": "", "style": "", "sd_prompt": ""} | |
| lines = text.splitlines() | |
| current = None | |
| buf = [] | |
| for ln in lines: | |
| ln_strip = ln.strip() | |
| if not ln_strip: continue | |
| if ln_strip.upper().startswith("SYMBOLS:"): | |
| if current: result[current] = "\n".join(buf).strip() | |
| current = "symbols"; buf = []; continue | |
| if ln_strip.upper().startswith("STYLE:"): | |
| if current: result[current] = "\n".join(buf).strip() | |
| current = "style"; buf = []; continue | |
| if ln_strip.upper().startswith("SD_PROMPT:"): | |
| if current: result[current] = "\n".join(buf).strip() | |
| current = "sd_prompt"; buf = []; continue | |
| buf.append(ln_strip) | |
| if current: result[current] = "\n".join(buf).strip() | |
| result["sd_prompt"] = result["sd_prompt"].strip(' \"') | |
| return result | |
| # ================================================== | |
| # 3. دالة Groq الجديدة باستخدام LangChain | |
| # ================================================== | |
| def call_groq_completion(prompt: str) -> str: | |
| """استدعاء نقطة النهاية Groq Completion API باستخدام LangChain.""" | |
| if not GROQ_API_KEY: | |
| # استجابة وهمية (Mock) في حالة عدم وجود مفتاح | |
| return ( | |
| "SYMBOLS:\n- aged tree, lantern, winding path\n" | |
| "STYLE:\n- warm cinematic backlight, painterly brushwork\n" | |
| "SD_PROMPT:\n- \"An ancient tree by a winding path, warm lantern light," | |
| " cinematic composition, golden hour, dramatic rim lighting," | |
| " detailed textures, 35mm lens, high-detail, organic colors\"" | |
| ) | |
| try: | |
| llm = ChatGroq( | |
| temperature=0.7, | |
| model_name=GROQ_MODEL, | |
| groq_api_key=GROQ_API_KEY, | |
| ) | |
| messages = [ | |
| HumanMessage(content=prompt) | |
| ] | |
| response = llm.invoke(messages) | |
| if response.content: | |
| return response.content | |
| else: | |
| # يمكن أن يحدث هذا إذا كان هناك خطأ داخلي في Groq أو إذا لم ينتج محتوى | |
| raise ValueError("Groq returned an empty response content.") | |
| except Exception as e: | |
| raise ValueError(f"LLM Groq/LangChain Error: {type(e).__name__} - {str(e)}") | |
| # ================================================== | |
| # 4. دالة Gradio الأساسية (Endpoint) | |
| # ================================================== | |
| def generate_prompt(quote, author, culture, callback_url): | |
| """ | |
| الدالة الخلفية الرئيسية لـ Gradio. | |
| """ | |
| if not quote: | |
| return "", "", "", "No quote provided." | |
| # 1. تجميع الـ Prompt واستدعاء LLM | |
| # الآن assemble_llm_prompt معرفة في الأعلى، ولن يحدث NameError | |
| assembled = assemble_llm_prompt(quote, author or None, culture or None) | |
| try: | |
| llm_text = call_groq_completion(assembled) | |
| except ValueError as e: | |
| return "", "", "", f"LLM Generation Failed: {e}" | |
| parsed = parse_llm_output(llm_text) | |
| # 2. تجهيز النتيجة | |
| sd_prompt = parsed.get("sd_prompt") | |
| symbols = parsed.get("symbols") | |
| style = parsed.get("style") | |
| result = { | |
| "sd_prompt": sd_prompt, | |
| "symbols": symbols, | |
| "style": style, | |
| "raw": llm_text | |
| } | |
| # 3. معالجة Callback (باستخدام Gradio Client) | |
| callback_status = "no-callback" | |
| if callback_url and client_utils.is_valid_url(callback_url): | |
| try: | |
| client = Client(callback_url) | |
| client.predict( | |
| result, | |
| api_name="/receive" | |
| ) | |
| callback_status = "sent-via-gradio-client" | |
| except ImportError: | |
| callback_status = "failed: gradio_client not installed." | |
| except Exception as e: | |
| callback_status = f"failed-gradio-cb: {e}" | |
| return sd_prompt, symbols, style, callback_status | |
| # ================================================== | |
| # 5. واجهة Gradio UI | |
| # ================================================== | |
| with gr.Blocks(title="Prompt Agent – Pure Gradio") as demo: | |
| gr.Markdown("# **Wisdom → SD Prompt Generator (Groq Llama3.1-70B)**") | |
| gr.Markdown("هذه الواجهة تستخدم LangChain للاتصال الموثوق بـ Groq.") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| quote_in = gr.Textbox(label="Wisdom / Proverb", lines=3, placeholder="Enter the wisdom text here...") | |
| author_in = gr.Textbox(label="Author (optional)") | |
| culture_in = gr.Textbox(label="Culture / Region (optional)") | |
| callback_url_in = gr.Textbox(label="Callback URL (Other HF Space API path, e.g. https://user/space/api/receive)") | |
| gen_btn = gr.Button("Generate Prompt 🚀", variant="primary") | |
| with gr.Column(scale=3): | |
| sd_out = gr.Textbox(label="Stable Diffusion Prompt", lines=4) | |
| symbols_out = gr.Textbox(label="Suggested Symbols", lines=3) | |
| style_out = gr.Textbox(label="Style / Lighting / Composition", lines=3) | |
| cb_out = gr.Textbox(label="Callback Status") | |
| gen_btn.click( | |
| fn=generate_prompt, | |
| inputs=[quote_in, author_in, culture_in, callback_url_in], | |
| outputs=[sd_out, symbols_out, style_out, cb_out], | |
| api_name="generate" | |
| ) | |
| # ================================================== | |
| # 6. نقطة الدخول الرئيسية | |
| # ================================================== | |
| if __name__ == '__main__': | |
| demo.launch() | |