from langchain_google_genai import ChatGoogleGenerativeAI from langchain_community.tools.tavily_search import TavilySearchResults import requests import os from typing import List, TypedDict from langgraph.types import Command from typing import Literal, List from langgraph.graph import StateGraph, START, END from dotenv import load_dotenv load_dotenv() def get_platform_tips(state) -> Command[Literal['web_search']]: """Conduct a web search to find up-to-date information on how to write an effective post for the provided platform.""" tavily_tool=TavilySearchResults(max_results=5) tavily_output = tavily_tool.invoke(f"tips on how to write an effective post on {state['platform']}") prompt = f""" Summarize the tips provided in {tavily_output}. These tips will be used to generate a {state['platform']} post Output as plain text. """ response = model.invoke(prompt).content return Command(update={"tips": response}, goto="web_search") def web_search(state) -> Command[Literal['generate_post']]: """Conduct a web search to find up-to-date information about a provided topic to be used for a social media post.""" tavily_tool=TavilySearchResults(max_results=5) response = tavily_tool.invoke(state["topic"]) return Command(update={"tavily_results": response}, goto="generate_post") def generate_social_media_post(state) -> Command[Literal["evaluate_engagement"]]: """Generate a social media post for a B2B bank.""" prompt = f""" You are a social media strategist for a B2B bank. Generate a {state["platform"]} post. The post should: - Be engaging but professional. - Provide value to corporate clients. - Focus on {state["topic"]}. - Incorporate information from {state["tavily_results"]} Output as plain text. """ response = model.invoke(prompt) return Command(update={"post": response.content}, goto="evaluate_engagement") def evaluate_engagement(state) -> Command[Literal["evaluate_tone"]]: """Assess how engaging the post is for LinkedIn/Instagram.""" prompt = f""" Score the following post on engagement (1-10) basd on the provided social media platform. Consider clarity, readability, and compelling call-to-action. Platform: {state["platform"]} Post: {state["post"]} Respond with just a number. """ score = model.invoke(prompt) return Command(update={"engagement_score": score.content}, goto="evaluate_tone") def evaluate_tone(state) -> Command[Literal["evaluate_clarity"]]: """Check if the post maintains a professional yet engaging tone.""" prompt = f""" Score the post’s tone (1-10). Ensure it's: - Professional but not too rigid. - Trustworthy and aligned with B2B financial services. - Aligns with the specified platform. Platform: {state["platform"]} Post: {state["post"]} Respond with just a number. """ score = model.invoke(prompt) return Command(update={"tone_score": score.content}, goto="evaluate_clarity") def evaluate_clarity(state) -> Command[Literal["revise_if_needed"]]: """Ensure the post is clear and not overly technical.""" prompt = f""" Score the post on clarity (1-10). - Avoids jargon. - Easy to read for busy corporate professionals. - Appropriate for the social media platform. Platform: {state["platform"]} Post: {state["post"]} Respond with just a number. """ score = model.invoke(prompt) return Command(update={"clarity_score": score.content}, goto="revise_if_needed") def revise_if_needed(state) -> Command[Literal["get_image"]]: """Revise post if average evaluation score is below a threshold.""" scores = [int(state["engagement_score"]), int(state["tone_score"]), int(state["clarity_score"])] avg_score = sum(scores) / len(scores) if avg_score < 7: # Arbitrary threshold for revision prompt = f""" Revise this post to improve clarity, engagement, and tone: {state["post"]} Improve based on the following scores: Engagement: {state["engagement_score"]} Tone: {state["tone_score"]} Clarity: {state["clarity_score"]} """ revised_post = model.invoke(prompt) return Command(update={"post": revised_post.content}, goto="get_image") return Command(goto="get_image") def fetch_image(state) -> Command[Literal[END]]: """Fetch an image from Unsplash based on the provided text.""" prompt = f""" You are a search optimization assistant. Your task is to take a topic and improve it to ensure the best image results from an image search API like Unsplash. Follow these steps: 1. **Normalize the input**: Convert all text to lowercase and remove special characters (except for spaces). 2. **Add more descriptive terms**: If the query is broad (e.g., "nature"), add more specific keywords like "landscape" or "outdoor" to help refine the search. 3. **Use synonyms and related terms**: For terms that could have multiple meanings or common synonyms, expand the query to include variations. For example, if the user queries "car", you can add "vehicle" or "automobile". 4. **Specify style and tone**: If the user provides a vague description, suggest adding words to define the style or mood of the image, such as "peaceful", "dramatic", or "colorful". 5. **Categorize the query**: If applicable, categorize the query into domains like "nature", "architecture", or "people" and add related terms (e.g., "urban", "portrait", "scenic"). **Example Inputs and Outputs:** 1. Input: "sunset over a beach" Output: "sunset beach ocean horizon landscape" 2. Input: "car" Output: "car vehicle automobile road transport" 3. Input: "nature" Output: "nature landscape outdoor scenic green" Topic: {state['topic']} """ url = "https://api.pexels.com/v1/search" params = { "query": model.invoke(prompt).content, "per_page": 5, "page": 1 } headers = { "Authorization": os.getenv("PEXELS_API_KEY") } response = requests.get(url, headers=headers, params=params) if response.status_code == 200: data = response.json() urls = [] for _, photo in enumerate(data['photos']): urls.append(photo['url']) return Command(update={"image_url": urls}, goto=END) return Command(goto=END) class State(TypedDict): topic: str platform: str tips: str tavily_results: List[dict] post: str engagement_score: int tone_score: int clarity_score: int image_url: str model = ChatGoogleGenerativeAI( model="gemini-1.5-flash", temperature=0, max_tokens=None, timeout=None, max_retries=2, ) workflow = StateGraph(State) workflow.add_node("get_tips", get_platform_tips) workflow.add_node("web_search", web_search) workflow.add_node("generate_post", generate_social_media_post) workflow.add_node("evaluate_engagement", evaluate_engagement) workflow.add_node("evaluate_tone", evaluate_tone) workflow.add_node("evaluate_clarity", evaluate_clarity) workflow.add_node("revise_if_needed", revise_if_needed) workflow.add_node("get_image", fetch_image) workflow.add_edge(START, "get_tips") graph = workflow.compile()