#!/usr/bin/env python3
"""
LinkedIn Post Generator - Gradio App
Based on the Jupyter notebook for generating LinkedIn posts from news articles
"""
import gradio as gr
import time
import copy
from datetime import date, timedelta, timezone, datetime
import json
import csv
import ast
import os
import sys
import re
import pandas as pd
import numpy as np
import logging
import requests
from bs4 import BeautifulSoup
from io import BytesIO
from PyPDF2 import PdfReader
import urllib.parse
import dateutil.parser
from dateutil import parser as dateutil_parser
from tldextract import extract
from urllib.parse import quote_plus
from collections import defaultdict
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
from utils import clean_url, get_body,ner_tagger,remove_duplicate_relationships
news_selector=2
# Set up logging
logging.basicConfig(level=logging.INFO)
os.environ["HF_HOME"] = "/tmp/huggingface"
os.environ["TRANSFORMERS_CACHE"] = "/tmp/huggingface/transformers"
os.environ["HF_DATASETS_CACHE"] = "/tmp/huggingface/datasets"
masterQuery = '"FinTech Agents" OR "Finance Agents" OR "Cognitive Agents" OR "Investment Agents" OR "AI Agents" OR "Autonomous Agents" OR "Agentic AI"'
goal="maximum engagement and SEO"
from gliner import GLiNER
# Import your existing libraries (make sure these are installed)
try:
from huggingface_hub import login
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, TransformersModel
from smolagents import WebSearchTool
from tldextract import extract
from dotenv import load_dotenv
from connector import get_google_news, get_news_articles
except ImportError as e:
print(f"Missing required library: {e}")
print("Please install required packages:")
print("pip install gradio huggingface_hub smolagents tldextract GoogleNews gliner feedparser pandas numpy requests beautifulsoup4 python-dateutil python-dotenv")
sys.exit(1)
# Global variables
ner_model = None
agent = None
writer_agent= None
editor_agent=None
# Load configuration from environment
HF_TOKEN = os.getenv('HF_TOKEN')
openai_key=os.getenv('OPENAI')
DEFAULT_INTERESTS = os.getenv('INTERESTS', 'cognition, sentience, finance, investing, orchestration')
USE_LOCAL_MODELS = os.getenv('USE_LOCAL_MODELS', 'false').lower() == 'true'
# Check if HF_TOKEN is available
if not HF_TOKEN:
print("❌ HuggingFace token not found. Please check your .env file.")
def check_environment():
"""Check if required environment variables are set"""
if not HF_TOKEN:
return False, "⌠HF_TOKEN not found in .env file. Please add your HuggingFace token."
return True, f"✅ Environment configured successfully!\n🔑 HF Token: {'*' * 20}{HF_TOKEN[-4:] if len(HF_TOKEN) > 4 else '****'}\n🎯 Interests: {DEFAULT_INTERESTS}\n🖥️ Use Local Models: {USE_LOCAL_MODELS}"
def initialize_models(use_local=None, interests=None):
"""Initialize the AI models and agents"""
global agent, writer_agent, editor_agent, ner_model
# Use environment defaults if not provided
if use_local is None:
use_local = USE_LOCAL_MODELS
if interests is None:
interests = DEFAULT_INTERESTS
# Check if HF_TOKEN is available
if not HF_TOKEN:
return "❌ HuggingFace token not found. Please check your .env file."
try:
# Login to HuggingFace
login(HF_TOKEN, add_to_git_credential=False)
# Initialize NER model
print("Initialize NER")
ner_model = GLiNER.from_pretrained("knowledgator/modern-gliner-bi-large-v1.0")
print(f"Initialized NER")
llm_engine = InferenceClientModel(
api_key=HF_TOKEN,
model_id="Qwen/Qwen3-Coder-480B-A35B-Instruct" ,
timeout=3000,
provider="fireworks-ai",
temperature=0.25
)
# Initialize agent
agent = CodeAgent(
model=llm_engine,
tools=[],
add_base_tools=False,
name="data_agent",
description="Runs data analysis for you.",
max_steps=1,
)
# Initialize agent
writer_agent = CodeAgent(
model=llm_engine,
tools=[],
add_base_tools=False,
name="writer_agent",
description="Write an engaging and creative LinkedIn post.",
max_steps=5,
)
writer_engine = InferenceClientModel(
api_key=HF_TOKEN,
model_id="Qwen/Qwen3-Coder-480B-A35B-Instruct" ,
timeout=3000,
provider="fireworks-ai",
temperature=0.4
)
# Initialize agent
editor_agent = CodeAgent(
model=writer_engine,
tools=[],
add_base_tools=False,
name="editor_agent",
description="Edits LinkedIn post.",
max_steps=5,
)
# Add system prompt
#system_prompt = f"You are a strategic digital marketing manager focused on improving my social footprint. My interests are {interests}. You will receive a social media post. Please let me know which one I should react on."
#agent.prompt_templates["system_prompt"] += system_prompt
return "✅ Models initialized successfully!"
except Exception as e:
return f"⌠Error initializing models: {str(e)}"
def initialize_editor():
"""Initialize the AI models and agents"""
# Check if HF_TOKEN is available
if not HF_TOKEN:
return "⌠HuggingFace token not found. Please check your .env file."
try:
# Login to HuggingFace
login(HF_TOKEN, add_to_git_credential=False)
# Initialize NER model
writer_engine = InferenceClientModel(
api_key=HF_TOKEN,
model_id="Qwen/Qwen3-235B-A22B-Thinking-2507" ,
timeout=3000,
provider="fireworks-ai",
temperature=0.4
)
# Initialize agent
editor_agent = CodeAgent(
model=writer_engine,
tools=[],
add_base_tools=False,
name="editor_agent",
description="Edits LinkedIn post.",
max_steps=5,
)
print(type(editor_agent))
return editor_agent
except Exception as e:
return f"⌠Error initializing editor: {str(e)}"
def edit_single_article(post, edit_prompt):
"""Edit a single news article and generate LinkedIn post"""
global editor_agent
if editor_agent is None:
return "⌠Editor model not initialized. Please initialize models first."
tmp_post=post['linkedin_post']
post.setdefault('history', []).append(tmp_post)
try:
prompt = f"""Role:
You are the editor agent.
Task:
• Adjust the provided post based on the requirements of the user.
Format:
• Input text will be wrapped in tags.
• Keep the original unless instructed otherwise.
• Edit only where requested.
• Use similar language and style as in the input.
Instructions:
1. Think step by step internally before answering.
2. Do not include your reasoning in the final output.
3. If uncertain, return the original post.
4. Apply the user's edit instructions while maintaining the professional LinkedIn tone.
5. Keep the post length appropriate for LinkedIn (250-450 words).
6. If the user requests to check the original document use the "body" tag in the input.
User Edit Instructions: {edit_prompt}
Input:
{tmp_post}
Output:
[Your edited post goes here.]
"""
edited_response = editor_agent.run(prompt, reset=False)
print(type(post))
post['linkedin_post'] = edited_response
with open('edit_output.json', 'w') as f:
json.dump(post, f, indent=2)
except Exception as e:
print(f"Error editing post: {e}")
#return f"⌠Error editing post: {str(e)}"
return post
def process_single_article(post, interests):
"""Process a single news article and generate LinkedIn post"""
global agent, writer_agent, ner_model, editor_agent
try:
# Login to HuggingFace
login(HF_TOKEN, add_to_git_credential=False)
# Initialize NER model
print("Initialize NER")
ner_model = GLiNER.from_pretrained("knowledgator/modern-gliner-bi-large-v1.0")
print(f"Initialized NER")
llm_engine = InferenceClientModel(
api_key=HF_TOKEN,
model_id="Qwen/Qwen3-Coder-480B-A35B-Instruct" ,
timeout=3000,
provider="fireworks-ai",
temperature=0.25
)
# Initialize agent
agent = CodeAgent(
model=llm_engine,
tools=[],
add_base_tools=False,
name="data_agent",
description="Runs data analysis for you.",
max_steps=1,
)
# Initialize agent
writer_agent = CodeAgent(
model=llm_engine,
tools=[],
add_base_tools=False,
name="writer_agent",
description="Write an engaging and creative LinkedIn post.",
max_steps=5,
)
writer_engine = InferenceClientModel(
api_key=HF_TOKEN,
model_id="Qwen/Qwen3-Coder-480B-A35B-Instruct" ,
timeout=3000,
provider="fireworks-ai",
temperature=0.4
)
# Initialize agent
editor_agent = CodeAgent(
model=writer_engine,
tools=[],
add_base_tools=False,
name="editor_agent",
description="Edits LinkedIn post.",
max_steps=5,
)
# Add system prompt
#system_prompt = f"You are a strategic digital marketing manager focused on improving my social footprint. My interests are {interests}. You will receive a social media post. Please let me know which one I should react on."
#agent.prompt_templates["system_prompt"] += system_prompt
print("… Models initialized successfully!")
except Exception as e:
print( f"! Error initializing models: {str(e)}")
if agent is None or ner_model is None or writer_agent is None or editor_agent is None:
return {"error": f"Models agent {agent}, ner_model {type(ner_model)} write_agent {writer_agent}, editor_agent {editor_agent} not initialized. Please initialize models first."}
#try:
title = post['title']
media = post['media']
date = post['date']
desc = post['desc']
url = clean_url(post['link'])
domain = post['domain']
# Get article body
body = get_body(url)
if not body:
return {"error": f"Could not extract content from {url}"}
# Extract named entities
ner_tags = ner_tagger(body, ner_model)
if len(ner_tags) == 0:
return {"error": "No named entities found in article"}
# Generate body summary
#prompt1 = f"You are a reasoning agent. Write a concise and accurate 150 words summary of this text : {body}!"
prompt1= f"""Role:
You are a reasoning agent skilled at summarizing text accurately and concisely.
Task:
Summarize the following text in ~150 words.
Format:
Input text will be wrapped in tags.
Output summary must be wrapped in tags.
Use clear, neutral, and structured language.
Instructions:
Think step by step internally before answering.
Do not include your reasoning in the final summary.
Input:
{body}
Output:
[Your ~150-word summary goes here.]
"""
body_summary = agent.run(prompt1, reset=True)
# Generate NER summary
#prompt1_ner = f"You are a reasoning agent. Write a concise 150 words narrative of the relationships in these named entities : {ner_tags}?"
prompt1_ner= f"""
Role:
You are a reasoning agent skilled at writing concise narratives.
Task:
Write a ~150-word narrative describing the relationships among the named entities provided.
Format:
Input entities will be wrapped in tags.
Output narrative must be wrapped in