Spaces:
Sleeping
Sleeping
| import os | |
| import streamlit as st | |
| from openai import OpenAI | |
| from dotenv import load_dotenv | |
| from reportlab.pdfgen import canvas | |
| import base64 | |
| from token_optimizer import optimize_prompt | |
| # ------------------------ | |
| # 🔑 تنظیم کلاینت OpenAI-compatible (Hugging Face) | |
| # ------------------------ | |
| load_dotenv() | |
| client = OpenAI( | |
| base_url=os.getenv("OPENROUTER_BASE_URL"), | |
| api_key=os.getenv("OPENROUTER_API_KEY") | |
| ) | |
| # ------------------------ | |
| # 🎨 استایل اختیاری | |
| # ------------------------ | |
| with open("src/theme.css") as f: | |
| st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True) | |
| # ------------------------ | |
| # Page setup | |
| # ------------------------ | |
| st.set_page_config(page_title="AI Article Writer", page_icon="🧠", layout="centered") | |
| st.title("🧠 AI Article Writer") | |
| st.subheader("تولید مقاله حرفهای با هوش مصنوعی") | |
| st.divider() | |
| # ------------------------ | |
| # Inputs | |
| # ------------------------ | |
| topic = st.text_input("موضوع مقاله:",key="topic_input", placeholder="مثلاً تاثیر هوش مصنوعی بر آموزش...") | |
| tone = st.selectbox("لحن مقاله:", ["علمی", "غیررسمی", "تخصصی", "داستانی", "تحلیلی", "احساسی", "طنزآمیز ", "ژورنالیستی"], key="tone_select") | |
| language = st.selectbox("زبان مقاله:", ["فارسی", "انگلیسی"], key="lang_select") | |
| length = st.select_slider("طول مقاله(پاراگراف):", options=list(range(1,11)), value=3, key="length_slider") # 1 to 10 scale | |
| keywords = st.text_input("کلمات کلیدی (اختیاری):",key="keywords_input", placeholder="مثلاً آموزش، فناوری، هوش مصنوعی...") | |
| references_num = st.number_input("تعداد منابع مورد استفاده (اختیاری):", 0, 5, 1, key="ref_num") | |
| references_condition = st.text_input("شرایط منابع (اختیاری):",key="ref_cond", placeholder="مثلاً منابع معتبر و بهروز...") | |
| st.divider() | |
| # ------------------------ | |
| # Model selection | |
| # ------------------------ | |
| model_options = { | |
| "🦾 مدل بزرگ، مناسب تولید محتوای باکیفیت و خلاقانه (Meta-Llama Maverick)": | |
| "meta-llama/llama-4-maverick:free", | |
| "✍️ مدل 24B، مناسب مقالهنویسی و متنهای طولانی با انسجام بالا (Mistral-Small 3.2)": | |
| "mistralai/mistral-small-3.2-24b-instruct:free", | |
| "🚀 مدل خیلی بزرگ، مناسب کارهای سنگین، تحلیل عمیق و تولید محتوای حرفهای (Hermes 405B)": | |
| "nousresearch/hermes-3-llama-3.1-405b:free", | |
| "⚡ سریع و سبک، مناسب متننویسی روزمره و پاسخهای کوتاه (DeepHermes 8B)": | |
| "nousresearch/deephermes-3-llama-3-8b-preview:free", | |
| # "nvidia/nemotron-nano-12b-v2-vl:free":"nvidia/nemotron-nano-12b-v2-vl:free", | |
| # "minimax/minimax-m2:free":"minimax/minimax-m2:free", | |
| } | |
| selected_label = st.selectbox("مدل مورد استفاده:", list(model_options.keys()), key="model_label_select") | |
| model_name = model_options[selected_label] | |
| # ------------------------ | |
| # Advanced settings | |
| # ------------------------ | |
| with st.expander("⚙️ تنظیمات پیشرفته (برای کاربران حرفهای)", expanded=False): | |
| st.caption("در صورت نیاز میتوانید تنظیمات مدل را تغییر دهید:") | |
| temperature = st.slider("میزان خلاقیت مدل (Temperature)", 0.1, 1.0, 0.3) | |
| max_tokens = st.slider("حداکثر تعداد توکن خروجی", 200, 2000, 800) | |
| st.divider() | |
| # ------------------------ | |
| # Generate button | |
| # ------------------------ | |
| if st.button("✍️ تولید مقاله"): | |
| if not topic.strip(): | |
| st.warning("لطفاً موضوع مقاله را وارد کنید 🌱") | |
| else: | |
| with st.spinner("در حال تولید مقاله..."): | |
| prompt = f""" | |
| You are an expert human writer and researcher. | |
| With'{references_condition}' on your mind, and using '{references_num}' credible sources if needed, | |
| Write a high-quality, well-researched article in '{language}' with a '{tone}' tone about the topic: '{topic}'. | |
| Requirements: | |
| - Follow SEO principles and naturally include these keywords if relevant: '{keywords}' | |
| - Structure: | |
| 1️⃣ A compelling title | |
| 2️⃣ An engaging introduction with a hook | |
| 3️⃣ Multiple well-organized paragraphs with clear subheaders | |
| 4️⃣ Examples, recent insights, or credible statistics when useful | |
| 5️⃣ A strong conclusion with a key takeaway | |
| - Style: | |
| - Human-like, smooth, and conversational writing (not robotic) | |
| - No hallucinated facts — ensure clarity and accuracy | |
| - Avoid repetition and filler content | |
| - Maintain logical flow and readability | |
| Length: | |
| - Write a '{length}' paragraph article. | |
| References: | |
| - If '{references_num}' > 0, include '{references_num}' credible sources or references at the end. | |
| Make the content professional, coherent, informative, and enjoyable to read. | |
| """ | |
| # بهینهسازی پرامپت قبل از ارسال به مدل | |
| prompt = optimize_prompt(prompt) | |
| try: | |
| response = client.chat.completions.create( | |
| model=model_name, | |
| messages=[ | |
| {"role": "system", "content": "You are a professional article writer."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| temperature=temperature, | |
| max_tokens=max_tokens | |
| ) | |
| article_text = response.choices[0].message.content | |
| st.success("✅ مقاله تولید شد:") | |
| st.text_area("📄 متن مقاله:", value=article_text, height=400) | |
| # ✅ دکمه دانلود TXT | |
| b64 = base64.b64encode(article_text.encode()).decode() | |
| href = f'<a href="data:file/txt;base64,{b64}" download="article.txt">📥 دانلود نسخه متنی</a>' | |
| st.markdown(href, unsafe_allow_html=True) | |
| # ✅ دکمه دانلود PDF | |
| # ✅ ذخیره مقاله تولیدشده در حافظه موقت Session | |
| if "articles_session" not in st.session_state: | |
| st.session_state.articles_session = [] | |
| st.session_state.articles_session.append({ | |
| "topic": st.session_state.get("topic_input", topic), | |
| "tone": st.session_state.get("tone_select", tone), | |
| "language": st.session_state.get("lang_select", language), | |
| "length": st.session_state.get("length_slider", length), | |
| "keywords": st.session_state.get("keywords_input", keywords), | |
| "references_num": st.session_state.get("ref_num", references_num), | |
| "references_condition": st.session_state.get("ref_cond", references_condition), | |
| "model_label": st.session_state.get("model_label_select", selected_label), | |
| "content": article_text, | |
| }) | |
| st.info(f"📌 مقاله ذخیره شد — تعداد {len(st.session_state.articles_session)} مورد") | |
| except Exception as e: | |
| st.error(f"⚠️ خطا در فراخوانی مدل: {e}") | |
| # 🗂️ نمایش نتایج قبلی (موقت) | |
| if st.session_state.get("articles_session"): | |
| st.subheader("🗂️ نتایج قبلی (تا زمان بسته شدن صفحه)") | |
| for i, item in enumerate(st.session_state.articles_session): | |
| with st.expander(f"📄 مقاله {i+1}: {item['topic']}"): | |
| st.markdown(f"**موضوع:** {item['topic']}") | |
| st.markdown(f"**لحن:** {item['tone']}") | |
| st.markdown(f"**زبان:** {item['language']}") | |
| st.markdown(f"**طول مقاله:** {item['length']} پاراگراف") | |
| st.markdown(f"**کلمات کلیدی:** {item['keywords']}") | |
| st.markdown(f"**تعداد منابع:** {item['references_num']}") | |
| st.markdown(f"**شرایط منابع:** {item['references_condition']}") | |
| st.markdown(f"**مدل استفاده شده:** {item['model_label']}") | |
| st.markdown("---") | |
| st.write(item["content"]) | |
| # 🧭 اجرا: | |
| # streamlit run src/article_writer.py | |