# === 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()