import os
import time
import logging
import pandas as pd
#from google import genai
#import google.generativeai as genai
from google import genai
from google.genai import types
from google.genai.types import Tool, GenerateContentConfig, GoogleSearch
from dotenv import load_dotenv
from datetime import datetime
def get_improved_titles_from_gemini(pvt_title_df, config, gpt_language):
"""
Uses Google's Gemini models to improve tag titles based on top queries.
Args:
pvt_title_df (pd.DataFrame): DataFrame containing 'page', 'tag title', and 'top_queries'.
config: Configuration object with settings like 'counter_max'.
gpt_language (str): The language to use for the prompt.
Returns:
pd.DataFrame: The input DataFrame with an added 'new tag title' column.
"""
if "new tag title" not in pvt_title_df.columns:
pvt_title_df["new tag title"] = None
dotenv_path = os.path.join(os.path.dirname(__file__), 'credenziali', '.env')
load_dotenv(dotenv_path)
api_key = os.getenv('GEMINI_API_KEY')
if not api_key:
logging.error("GEMINI_API_KEY not found in .env file or environment variables. Skipping title improvement.")
return pvt_title_df
try:
client = genai.Client(api_key=api_key)
logging.info('Gemini client created successfully.')
except Exception as e:
logging.error(f"Failed to create Gemini client: {e}")
return pvt_title_df
gemini_responses = {}
llm_api_calls = 0
# update 20250827
total_tokens_used = 0
def improve_tag_title(row, gemini_responses):
nonlocal total_tokens_used, llm_api_calls
page_url = row["page"]
tag_title = row["tag title"]
top_queries = row['top_queries']
if page_url in gemini_responses:
return gemini_responses[page_url], False
prompt = f"""Sei un copywriter SEO e un analista di search intent di livello mondiale, con una profonda esperienza nell'ottimizzare elementi on-page per i motori di ricerca come Google.
#### OBIETTIVO
Il tuo obiettivo è creare il miglior tag title SEO possibile per la pagina web fornita, massimizzando il potenziale di ranking organico e il Click-Through Rate (CTR).
#### CONTESTO
- URL da aprire ed **Analizzare:** '{page_url}'
- **Tag Title Attuale (da migliorare):** '{tag_title}'
- **Query Principali (in ordine di importanza):** '{top_queries}'
- **Devi scrivere il tag title in Lingua:** '{gpt_language}'
#### **PROCESSO DI ANALISI E CREAZIONE**
1. **Analisi Approfondita del Contenuto e dell'Intento:**
* Leggi e analizza l'intero contenuto della pagina all'URL fornito.
* Identifica l'argomento principale, gli argomenti secondari, e soprattutto, l'**intento di ricerca primario** che la pagina soddisfa (es. informativo "come fare", commerciale "migliori x", transazionale "compra y", di navigazione "login pagina z").
* Comprendi il valore unico o il beneficio chiave che la pagina offre al lettore.
2. **Estrazione e Prioritizzazione delle Parole Chiave:**
* Estrai le parole chiave più pertinenti direttamente dal testo, dai titoli (H1, H2, etc.), e dalla struttura generale della pagina.
* Determina la **query principale** (la più importante e rappresentativa) e le **query secondarie** (che aggiungono contesto e specificità).
3. **Creazione del Tag Title:**
* Basandoti sull'analisi, formula un tag title che:
* Includa la query principale all'inizio, o il più vicino possibile.
* Integri in modo naturale una o più query secondarie, se lo spazio lo consente e aumenta la pertinenza.
* Sia **persuasivo**, generando curiosità o comunicando un beneficio chiaro per invogliare al clic.
#### **REGOLE INDEROGABILI**
* **Lunghezza:** Massimo **68 caratteri**, spazi inclusi.
* **Brand:** **Non includere MAi il nome del brand**, a meno che non sia l'argomento centrale della pagina (es. una recensione di un prodotto specifico).
* **Caratteri Speciali:** Usa solo lettere, numeri, e se necessario, il trattino `-` o i due punti `:` come separatori logici. Assolutamente vietati emoji, simboli come ©, ®, ™.
* **Virgolette:** Non racchiudere il NUOVO tag title tra virgolette.
* **Keyword Stuffing:** Evita la ripetizione forzata e innaturale delle parole chiave.
* **Focus:** Il tag title deve riflettere fedelmente e in modo accattivante l'intento ed il contenuto reale della pagina.
#### **OUTPUT FINALE**
* **Caso di Successo:** Restituisci **SOLO ED ESCLUSIVAMENTE** il nuovo tag title ottimizzato. Non scrivere NIENTE altro. Nessuna introduzione, nessuna spiegazione, nessun commento, nessuna etichetta come "Output:".
* **Caso di Errore:** Se non riesci ad accedere o analizzare l'URL per qualsiasi motivo, restituisci **SOLO ED ESCLUSIVAMENTE**: "ERRORE". Non scrivere NIENTE altro.
* **Esempio Output di Successo:** Guida completa alla compilazione del Modulo 730: istruzioni e scadenze
* **Esempio Output di Errore:** Impossibile accedere alla pagina
"""
tools = []
tools.append(Tool(url_context=types.UrlContext))
tools.append(Tool(google_search=types.GoogleSearch))
try:
# Usa il client con parametri di configurazione
response = client.models.generate_content(
# MODELS
# https://ai.google.dev/gemini-api/docs/models/
model="gemini-2.5-flash",
# gemini-2.5-pro
# gemini-2.5-flash
contents=prompt,
# 2. Passare lo strumento nella configurazione
config=types.GenerateContentConfig(
tools=tools,
temperature=0.5, # Controllo casualità (0.0-2.0)
top_p=0.9, # Nucleus sampling (0.0-1.0)
top_k=40, # Top-k sampling (integer)
max_output_tokens=150, # Token massimi
#presence_penalty=0.2, # Penalità presenza (-2.0 a +2.0)
#frequency_penalty=0.0, # Penalità frequenza (-2.0 a +2.0)
#stop_sequences=["STOP"], # Sequenze di stop (max 5)
candidate_count=1, # Numero candidati (1-8)
seed=42, # Seed per determinismo
response_mime_type="text/plain", # Formato risposta
# Per Gemini 2.5:
thinking_config=types.ThinkingConfig(
thinking_budget=0 # Disabilita thinking per velocità
)
)
)
llm_api_calls += 1
#total_tokens_used += response.usage_metadata.thoughts_token_count + response.usage_metadata.candidates_token_count
total_tokens_used += response.usage_metadata.total_token_count
improved_tag_title = response.text
gemini_responses[page_url] = improved_tag_title
time.sleep(1)
return improved_tag_title, True
except Exception as e:
logging.error(f"Error occurred while calling Gemini API: {e}")
return None, False
pvt_title_df.reset_index(drop=True, inplace=True)
titoli_inseriti = 0
processed_rows = 0
skipped_rows = 0
for index, row in pvt_title_df.iterrows():
if titoli_inseriti >= config.counter_max:
logging.info(f"Raggiunto il limite massimo di {config.counter_max} chiamate API. Interrompo l'elaborazione.")
break
tag_title = row["tag title"]
if pd.isna(tag_title) or tag_title == "":
skipped_rows += 1
continue
new_tag_title, is_new = improve_tag_title(row, gemini_responses)
if new_tag_title:
pvt_title_df.at[index, "new tag title"] = new_tag_title
if is_new:
titoli_inseriti += 1
processed_rows += 1
logging.info(f"URL: {row['page']}")
logging.info(f"Tag title: {tag_title}")
logging.info(f"Top queries: {row['top_queries']}")
logging.info(f'Nuovo tt: {new_tag_title}')
logging.info(f"API calls {titoli_inseriti} | {total_tokens_used} tokens.")
logging.info(f"Processed rows: {processed_rows}/{config.counter_max} | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
logging.info('')
logging.warning(f"Gemini API calls made: {llm_api_calls}")
logging.warning(f"Tag title inseriti: {titoli_inseriti}")
logging.warning(f"Rows skipped due to missing tag title: {skipped_rows}")
# 20250827 UPDATE
# ...
logging.warning(f"Total tokens used: {total_tokens_used}")
return pvt_title_df, llm_api_calls, total_tokens_used
I tested pro and flash, same stuff