Spaces:
Sleeping
Sleeping
| # filename: linkedin_caption_gemini_streamlit.py | |
| import streamlit as st | |
| from langchain_core.prompts import PromptTemplate | |
| from langchain_core.output_parsers import StrOutputParser | |
| from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint | |
| from langchain_core.runnables import RunnableLambda | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| # Initialize HF endpoint (Gemini 2.5 Flash or Zephyr) | |
| llm_endpoint = HuggingFaceEndpoint( | |
| repo_id="HuggingFaceH4/zephyr-7b-beta", | |
| temperature=0.3, | |
| ) | |
| model = ChatHuggingFace(llm=llm_endpoint) | |
| # Step 1: Draft prompt (human-friendly + style variable) | |
| template1 = PromptTemplate( | |
| template=""" | |
| You are an experienced LinkedIn content writer. | |
| Write a professional, friendly, and engaging LinkedIn caption based on the topic below. | |
| Make it feel personal and human-written. | |
| Topic: {topic} | |
| Style: {style} | |
| Guidelines: | |
| - Keep it concise (3-5 sentences) | |
| - Invite readers to connect or collaborate | |
| - Add 5-7 relevant, natural hashtags | |
| - Do not repeat hashtags | |
| - Make it sound like a real human wrote it | |
| """, | |
| input_variables=["topic", "style"], | |
| ) | |
| # Step 2: Refinement prompt | |
| template2 = PromptTemplate( | |
| template="Refine the following text to match style: {style}\n\nText:\n{text}", | |
| input_variables=["text", "style"], | |
| ) | |
| parser = StrOutputParser() | |
| # RunnableLambda to convert draft string to dict expected by template2 | |
| wrap_for_refine = RunnableLambda(lambda draft, style: {"text": draft, "style": style}) | |
| # Streamlit UI | |
| st.title("LinkedIn Caption Generator ๐") | |
| topic_input = st.text_area("Enter your post topic / idea:") | |
| style_option = st.selectbox("Choose caption style:", ["Official", "Exciting", "No-fluff", "Storytelling"]) | |
| if st.button("Generate Caption"): | |
| if not topic_input.strip(): | |
| st.warning("Please enter some content first!") | |
| else: | |
| with st.spinner("Generating caption..."): | |
| # Build the chain dynamically with style bound | |
| chain = ( | |
| template1 | |
| | model | |
| | parser | |
| | wrap_for_refine.bind(style=style_option) | |
| | template2 | |
| | model | |
| | parser | |
| ) | |
| # Pass both topic and style for template1 | |
| result = chain.invoke({"topic": topic_input, "style": style_option}) | |
| st.subheader("Generated Caption:") | |
| st.success(result) | |