news-summarizer / app.py
wwydmanski's picture
Create app.py
543780f verified
import logging
import os
from logging import getLogger
import gradio as gr
from google import genai
from google.genai import types
logger = getLogger(__name__)
# Configure logger to show info level messages
logging.basicConfig(level=logging.INFO)
logger.setLevel(logging.INFO)
logger.info("Logger configured to show INFO level messages")
def get_sources(response):
"""Zbiera źródła z groundingMetadata"""
try:
if not response.candidates or not response.candidates[0].grounding_metadata:
logger.info("Brak grounding metadata - brak źródeł")
return []
grounding_meta = response.candidates[0].grounding_metadata
chunks = grounding_meta.grounding_chunks
if not chunks:
logger.info("Brak chunks - brak źródeł")
return []
# Zbierz unikalne źródła z URI
sources = []
seen_uris = set()
for chunk in chunks:
if chunk.web and chunk.web.uri:
uri = chunk.web.uri
if uri not in seen_uris:
seen_uris.add(uri)
# Spróbuj wyciągnąć tytuł z chunk jeśli dostępny
title = getattr(chunk.web, 'title', '') if hasattr(chunk.web, 'title') else ''
if not title:
# Jeśli brak tytułu, użyj domeny z URL
from urllib.parse import urlparse
parsed = urlparse(uri)
title = parsed.netloc or uri
sources.append({
'title': title,
'url': uri
})
logger.info(f"Znaleziono {len(sources)} unikalnych źródeł")
return sources
except Exception as e:
logger.error(f"Błąd podczas zbierania źródeł: {e}", exc_info=True)
return []
def format_sources_section(sources):
"""Formatuje sekcję źródeł"""
if not sources:
return ""
sources_text = "\n\n## Źródła:\n"
for i, source in enumerate(sources, 1):
sources_text += f"{i}. [{source['title']}]({source['url']})\n"
return sources_text
def generate(link: str, request: gr.Request):
"""Funkcja pobierająca link od użytkownika i generująca streszczenie."""
# Pokaż loading state na początku
yield "🔄 **Generuję streszczenie artykułu...**\n\nProszę czekać, może to potrwać kilka chwil."
try:
client = genai.Client(
vertexai=False,
api_key=os.getenv("GOOGLE_API_KEY")
)
prompt = f"""
Otrzymujesz link do artykułu. Twoje zadanie to przygotowanie syntetycznego streszczenia na potrzeby YouTube oraz social media.
Zwróć wynik w następującej strukturze:
## Tytuł:
Przepisz dokładnie tytuł artykułu
## Podsumowanie:
Jedno-dwa zdania, które jasno wyjaśniają, czego dotyczy artykuł.
## Sugestie do odcinka YouTube:
Wypunktuj rzeczy, które warto poruszyć w odcinku (najważniejsze wątki, ciekawe fakty, potencjalne tematy do rozwinięcia):
- Opis tematu - dodatkowe wyjaśnienie
- Kolejny temat - kontekst
## Najważniejsze informacje:
Zbierz kluczowe dane i fakty, nie pomijaj liczb, wyników, cytowanych źródeł:
- Konkretny fakt/liczba/wynik - kontekst i wyjaśnienie
- Następna informacja - dodatkowe szczegóły
## Cytaty z artykułu:
Jeśli w artykule są cytaty bohaterów lub ekspertów, wypisz je w formacie:
> **"pełny cytat"** - imię i nazwisko osoby, stanowisko
Pogrub najważniejsze fragmenty cytatów. Nie wymyślaj cytatów, jeśli ich nie ma – pomiń ten punkt.
## Ciekawostki:
Jeśli w artykule pojawiają się ciekawe lub zaskakujące informacje, wypisz je tutaj. Możesz dodać dodatkowe fakty znalezione w internecie, pod warunkiem, że są aktualne i zgodne z treścią artykułu:
- Ciekawa informacja - dodatkowy kontekst
- Dodatkowy fakt z internetu - wyjaśnienie pochodzenia
## Timestamp do odcinka YouTube:
Zaproponuj timestamp w formacie:
- 00:00 **Wprowadzenie**
- 02:30 **Główne wątki**
- 05:00 **Najważniejsze fakty**
- 08:00 **Podsumowanie**
### ZASADY:
- Wszystko musi być zgodne z treścią artykułu
- Nie wymyślaj cytatów ani faktów
- Jeśli dany punkt nie występuje w artykule, pomiń go bez komentarza
- Używaj pogrubień (**tekst**) dla kluczowych informacji
- NIE dodawaj linków markdown - będą dodane automatycznie
Źródło: {link}
"""
model = "gemini-2.5-flash"
# Uproszczona konfiguracja - użyj prompt jako string
tools = [
types.Tool(google_search=types.GoogleSearch()),
]
generate_content_config = types.GenerateContentConfig(
temperature=0.0,
top_p=0.95,
max_output_tokens=8192,
tools=tools,
)
# Wygeneruj odpowiedź (bez streamingu)
response = client.models.generate_content(
model=model,
contents=prompt,
config=generate_content_config,
)
if response and response.text:
# Zbierz źródła z groundingMetadata
sources = get_sources(response)
# Loguj informacje o groundingu
if (response.candidates and response.candidates[0].grounding_metadata):
grounding_meta = response.candidates[0].grounding_metadata
if hasattr(grounding_meta, 'web_search_queries'):
logger.info(f"Zapytania wyszukiwania: {grounding_meta.web_search_queries}")
if hasattr(grounding_meta, 'grounding_chunks') and grounding_meta.grounding_chunks:
logger.info(f"Liczba źródeł: {len(grounding_meta.grounding_chunks)}")
# Połącz odpowiedź z sekcją źródeł
final_result = response.text + format_sources_section(sources)
yield final_result
else:
yield "❌ **Błąd:** Nie udało się wygenerować streszczenia. Spróbuj ponownie."
except Exception as e:
# Return error message
yield f"❌ **Błąd:** {str(e)}"
with gr.Blocks() as demo:
_ = gr.Markdown("## Streszczenia newsów\nWklej link do artykułu, a wygeneruję z niego najważniejsze informacje w formie podsumowania i punktów.")
with gr.Row():
link_input = gr.Textbox(label="Link do artykułu", placeholder="https://akademiatriathlonu.pl/...")
output = gr.Markdown(label="Streszczenie")
submit_btn = gr.Button("Generuj streszczenie")
_ = submit_btn.click(fn=generate, inputs=[link_input], outputs=[output])
_ = demo.launch(show_error=True)