Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- Dockerfile +7 -0
- agents.py +170 -0
Dockerfile
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11
|
| 2 |
+
WORKDIR /
|
| 3 |
+
COPY requirements.txt .
|
| 4 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 5 |
+
COPY . .
|
| 6 |
+
EXPOSE 5000
|
| 7 |
+
CMD ["gunicorn", "--bind", "0.0.0.0:7860", "test2:app"]
|
agents.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_google_genai import GoogleGenerativeAI
|
| 2 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 3 |
+
from langchain.chains import LLMChain
|
| 4 |
+
|
| 5 |
+
class SocialMediaAgents:
|
| 6 |
+
PLATFORM_LIMITS = {
|
| 7 |
+
"twitter": {"chars": 280, "words": None},
|
| 8 |
+
"instagram": {"chars": None, "words": 400},
|
| 9 |
+
"linkedin": {"chars": None, "words": 600},
|
| 10 |
+
"facebook": {"chars": None, "words": 1000}
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
def __init__(self, api_key: str):
|
| 14 |
+
"""Initialize the agent with a Google API key."""
|
| 15 |
+
self.llm = GoogleGenerativeAI(model="gemini-1.5-flash", google_api_key=api_key)
|
| 16 |
+
|
| 17 |
+
def _create_chain(self, template: str) -> LLMChain:
|
| 18 |
+
"""Create an LLM chain with the given prompt template."""
|
| 19 |
+
prompt = ChatPromptTemplate.from_template(template)
|
| 20 |
+
return LLMChain(llm=self.llm, prompt=prompt)
|
| 21 |
+
|
| 22 |
+
def _enforce_limits(self, text: str, platform: str) -> str:
|
| 23 |
+
"""Enforce platform-specific character or word limits."""
|
| 24 |
+
limits = self.PLATFORM_LIMITS[platform.lower()]
|
| 25 |
+
if limits["chars"] and len(text) > limits["chars"]:
|
| 26 |
+
return text[:limits["chars"]-3] + "..."
|
| 27 |
+
if limits["words"]:
|
| 28 |
+
words = text.split()
|
| 29 |
+
if len(words) > limits["words"]:
|
| 30 |
+
return " ".join(words[:limits["words"]]) + "..."
|
| 31 |
+
return text
|
| 32 |
+
|
| 33 |
+
# def twitter_transform(self, title: str, description: str) -> dict:
|
| 34 |
+
# """Transform content for Twitter."""
|
| 35 |
+
# link = "https://www.eye-on.ai/podcast-archive"
|
| 36 |
+
# template = """Transform this into a Twitter post (280 characters max):
|
| 37 |
+
# - Attention-grabbing message
|
| 38 |
+
# - 1-2 relevant hashtags
|
| 39 |
+
# - Essential information only
|
| 40 |
+
|
| 41 |
+
# Format output EXACTLY like this:
|
| 42 |
+
# New Title: [transformed title]
|
| 43 |
+
# ---
|
| 44 |
+
# New Description: [transformed description]
|
| 45 |
+
|
| 46 |
+
# add this line after descripttion and make link clickable listen to full podcast on {link}
|
| 47 |
+
|
| 48 |
+
# Original Content:
|
| 49 |
+
# Title: {title}
|
| 50 |
+
# Description: {description}"""
|
| 51 |
+
# chain = self._create_chain(template)
|
| 52 |
+
# response = chain.invoke({"title": title, "description": description, "link": link})
|
| 53 |
+
|
| 54 |
+
def twitter_transform(self, title: str, description: str,link:str) -> dict:
|
| 55 |
+
"""Transform content for Twitter with a clickable link and within 280 characters."""
|
| 56 |
+
template = """
|
| 57 |
+
Transform this into a Twitter post (max 280 characters total):
|
| 58 |
+
- Create an attention-grabbing single-line tweet using key info from the title and description
|
| 59 |
+
- Include 1-2 relevant hashtags
|
| 60 |
+
- End with this line exactly: Listen to full podcast: {link}
|
| 61 |
+
- Ensure the ENTIRE result is no more than 280 characters TOTAL (including the link line)
|
| 62 |
+
- if character more than 280 characters manage limit and exclude description character
|
| 63 |
+
- Don't short {link} i want full link
|
| 64 |
+
Return in this format:
|
| 65 |
+
New Title: [transformed title]
|
| 66 |
+
---
|
| 67 |
+
New Description: [tweet content]
|
| 68 |
+
|
| 69 |
+
Original Content:
|
| 70 |
+
Title: {title}
|
| 71 |
+
Description: {description}
|
| 72 |
+
"""
|
| 73 |
+
chain = self._create_chain(template)
|
| 74 |
+
response = chain.invoke({"title": title, "description": description, "link": link})
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
parts = response['text'].split('---')
|
| 79 |
+
result = {
|
| 80 |
+
"new_title": parts[0].replace('New Title:', '').strip(),
|
| 81 |
+
"new_description": parts[1].replace('New Description:', '').strip()
|
| 82 |
+
}
|
| 83 |
+
combined_text = f"{result['new_title']} {result['new_description']}"
|
| 84 |
+
limited_text = self._enforce_limits(combined_text, "twitter")
|
| 85 |
+
if len(limited_text) < len(combined_text):
|
| 86 |
+
result['new_title'] = ""
|
| 87 |
+
result['new_description'] = limited_text
|
| 88 |
+
return result
|
| 89 |
+
|
| 90 |
+
def instagram_transform(self, title: str, description: str) -> dict:
|
| 91 |
+
"""Transform content for Instagram."""
|
| 92 |
+
template = """Transform this into an Instagram post (400 words max):
|
| 93 |
+
- Catchy title with relevant emojis
|
| 94 |
+
- Engaging description
|
| 95 |
+
- 3-5 relevant hashtags
|
| 96 |
+
|
| 97 |
+
Format output EXACTLY like this:
|
| 98 |
+
New Title: [transformed title]
|
| 99 |
+
---
|
| 100 |
+
New Description: [transformed description]
|
| 101 |
+
|
| 102 |
+
Original Content:
|
| 103 |
+
Title: {title}
|
| 104 |
+
Description: {description}"""
|
| 105 |
+
chain = self._create_chain(template)
|
| 106 |
+
response = chain.invoke({"title": title, "description": description})
|
| 107 |
+
parts = response['text'].split('---')
|
| 108 |
+
result = {
|
| 109 |
+
"new_title": parts[0].replace('New Title:', '').strip(),
|
| 110 |
+
"new_description": parts[1].replace('New Description:', '').strip()
|
| 111 |
+
}
|
| 112 |
+
result['new_description'] = self._enforce_limits(result['new_description'], "instagram")
|
| 113 |
+
return result
|
| 114 |
+
|
| 115 |
+
def linkedin_transform(self, title: str, description: str,link) -> dict:
|
| 116 |
+
"""Transform content for LinkedIn."""
|
| 117 |
+
|
| 118 |
+
template = """Transform this into a LinkedIn post (600 words max):
|
| 119 |
+
- Professional title
|
| 120 |
+
- Detailed description with business insights
|
| 121 |
+
- 2-3 relevant hashtags
|
| 122 |
+
- Professional tone
|
| 123 |
+
- End with this line exactly: Listen to full podcast: {link}
|
| 124 |
+
- Don't change link format and words.
|
| 125 |
+
- Ensure the ENTIRE result is no more than 600 words TOTAL (including the link line)
|
| 126 |
+
- if character more than 600 words manage limit and exclude description character
|
| 127 |
+
|
| 128 |
+
Format output EXACTLY like this:
|
| 129 |
+
New Title: [transformed title]
|
| 130 |
+
---
|
| 131 |
+
New Description: [transformed description]
|
| 132 |
+
|
| 133 |
+
Original Content:
|
| 134 |
+
Title: {title}
|
| 135 |
+
Description: {description}"""
|
| 136 |
+
chain = self._create_chain(template)
|
| 137 |
+
response = chain.invoke({"title": title, "description": description, "link": link})
|
| 138 |
+
parts = response['text'].split('---')
|
| 139 |
+
result = {
|
| 140 |
+
"new_title": parts[0].replace('New Title:', '').strip(),
|
| 141 |
+
"new_description": parts[1].replace('New Description:', '').strip()
|
| 142 |
+
}
|
| 143 |
+
result['new_description'] = self._enforce_limits(result['new_description'], "linkedin")
|
| 144 |
+
return result
|
| 145 |
+
|
| 146 |
+
def facebook_transform(self, title: str, description: str) -> dict:
|
| 147 |
+
"""Transform content for Facebook."""
|
| 148 |
+
template = """Transform this into a Facebook post (1000 words max):
|
| 149 |
+
- Engaging title
|
| 150 |
+
- Conversational description
|
| 151 |
+
- Call to action for engagement
|
| 152 |
+
- 1-2 relevant hashtags
|
| 153 |
+
|
| 154 |
+
Format output EXACTLY like this:
|
| 155 |
+
New Title: [transformed title]
|
| 156 |
+
---
|
| 157 |
+
New Description: [transformed description]
|
| 158 |
+
|
| 159 |
+
Original Content:
|
| 160 |
+
Title: {title}
|
| 161 |
+
Description: {description}"""
|
| 162 |
+
chain = self._create_chain(template)
|
| 163 |
+
response = chain.invoke({"title": title, "description": description})
|
| 164 |
+
parts = response['text'].split('---')
|
| 165 |
+
result = {
|
| 166 |
+
"new_title": parts[0].replace('New Title:', '').strip(),
|
| 167 |
+
"new_description": parts[1].replace('New Description:', '').strip()
|
| 168 |
+
}
|
| 169 |
+
result['new_description'] = self._enforce_limits(result['new_description'], "facebook")
|
| 170 |
+
return result
|