I extended the PyLadies and R-Ladies bot with a new feature using the support of Google Gemini. If you want to learn more about the bots and the project behind them, here are two posts that I wrote about the bots and their architecture and the Awesome PyLadies' repository (as well as Awesome R-Ladies' repository).
Catchy titles and emojis are great, but sometimes they don’t fully capture the heart of a blog post. That’s why I introduced a new feature for the PyLadies and R-Ladies bot on Mastodon and Bluesky. The bot now also share short summaries of the blog posts, giving readers a reason to click and explore further.
Choosing the right tool was crucial, and Gemini ticked all the boxes I was looking for. First and foremost, I needed a powerful LLM. The bot crawls through a variety of content, some of which is quite technical. Gemini’s ability to grasp these nuances and generate meaningful summaries was essential. Plus, it’s incredibly easy to integrate. The plug-and-play approach meant I could get up and running quickly, and the modular design allows me to switch models down the line if needed. And let’s not forget the cost! Being a passion project, I needed a solution that wouldn’t break the bank. Gemini’s free tier fit the bill perfectly, especially since I’m working with public data.
Alternative text
Image showing the architecture of the bot. The bot’s backend resides in a GitHub repository. GitHub Actions serves as a CRON trigger, running on a regular schedule. The CRON trigger activates a Python script named “promote_blog_posts.py”. The script gathers RSS feed content from an RSS metadata pickle file containing basic information. Using the RSS feed text, the script interacts with Google AI Studio, where the Gemini API is connected. The script instructs the API to summarize the text and returns the summary. The script combines the title, author, hashtags, link, and summary to create a social media post. The post is then automatically published on Bluesky and Mastodon.
The whole system runs on a GitHub repository, with GitHub Actions acting as a daily trigger. When the trigger fires, my promote_blog_posts.py
script comes in. It gathers all the RSS feed content, figures out who’s next on the posting list, and grabs their latest unposted content. Here’s where Gemini comes in: the script sends the RSS feed text over to Google AI Studio, where I’ve set up the Gemini API. Gemini then works its magic, generating a short and (hopefully) engaging summary of the post. This summary, along with the title, author, hashtags, and link, is packaged into a post and sent out to Bluesky and Mastodon. And that’s it! But how do we get there from a technical perspective?
Implementing Gemini was surprisingly straightforward. Here’s a breakdown of the steps:
Alternative text
Image showing Google AI Studio landing page with an arrow in the top-left corner pointing to “Get API Key”.
This was as simple as heading over to Google AI Studio, clicking “Get API Key,” and copying the generated key. I stored this key securely in my environment variables and GitHub secrets, making sure it never touches GitHub directly.
A few lines of code were all it took. I imported the Gemini library, initialized the model using my API key, and chose the lightweight yet powerful Gemini 1.5 8B model. Then, I created a simple function to handle the summarization process. This function takes the RSS feed text, feeds it to the model with a prompt specifying the desired length and tone, and retrieves the generated summary. Of course, I added some safety checks to ensure the summary is appropriate and doesn’t go off the rails. And that’s it!
import google generativeai as genai
# Init model
genai.configure(api_key=os.environ['GEMINI_API_KEY'])
model = genai.GenerativeModel(model_name='gemini-1.5-flash')
def summarize_blog_post(text_from_rss_feed, model):
# Build your prompt
prompt = ["Summarize the content of the post in maximum 60 characters.",
"Be as concise as possible and be engaging.",
text_from_rss_feed]
# Get model response
response = model.generate_content(prompt)
# Perform additional checks and only accept those that are marked as OK
safety_ratings = response.candidates[0].safety_ratings
if all(rating.probability.name == 'NEGLIGIBLE' for rating in safety_ratings):
return response_cleaned
return ''
The final step was a minor adjustment to my GitHub Actions YAML file. All I did was add the Gemini API key, pulling it from my GitHub secrets.
name: Promote PyLadies' blog posts
on:
schedule:
- cron: '0 7 1-31/2 * *' # will only run on odd days #'0 7 */3 * *' # '0 7 */3 * *' #'0 0 1 * *' # "At 07:00 UTC every third day (which is 10 CET)"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout repo content
uses: actions/checkout@v2 # Checkout the repository content
- name: setup python
uses: actions/setup-python@v4
with:
python-version: '3.9.16' # Install the python version needed
- name: install python packages
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Execute py script for Bluesky 🦋
env:
PLATFORM: "bluesky"
COUNTER: "pyladies_counter_bluesky.txt"
ARCHIVE_DIRECTORY: "pyladies_archive_directory_bluesky"
IMAGES: "pyladies_images"
PICKLE_FILE: "pyladies_meta_data.pkl"
CLIENT_NAME: "pyladies_bot"
PASSWORD: ${{ secrets.BSKY_PYLADIES }}
USERNAME: ${{ secrets.BSKY_USERNAME_PYLADIES }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} # Add your Gemini API key
run: python src/promote_blog_post.py
- name: Commit files
id: commit
run: |
git config --local user.email "action@github.com"
git config --local user.name "github-actions"
git add --all
if [-z "$(git status --porcelain)"]; then
echo "::set-output name=push::false"
else
git commit -m "Add changes" -a
echo "::set-output name=push::true"
fi
shell: bash
- name: Push changes
if: steps.commit.outputs.push == 'true'
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.SECRET_WRITE }}
With that done, the bot was ready to start summarizing on schedule! And this is what it looks like:
Alternative text
Image showing a post on Bluesky by the PyLadies bot including a summary.
This whole experience was a great learning opportunity, reinforcing the fact that incorporating powerful LLMs doesn’t have to be a headache. The low-effort approach, combining GitHub Actions and Google AI Studio, saved me tons of time (and also financial resources).
With summarization in place, I’m already brainstorming ways to make the bot even more awesome. Here’s a sneak peek: