fix: separate hashtags from content in AI generation
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
- Remove hashtag instructions from AI prompts (content-only generation) - Add _split_hashtags() to extract any hashtags AI still includes - generate_content() now returns (content, hashtags, model) tuple - Prevents duplicate hashtags when publishing (content + hashtags field) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c1a8cb6183
commit
b73fcb59d1
@ -368,8 +368,8 @@ def social_publisher_generate():
|
||||
for key, default in defaults.items():
|
||||
context.setdefault(key, default)
|
||||
|
||||
content, model = social_publisher.generate_content(post_type, context, tone=tone)
|
||||
return jsonify({'success': True, 'content': content, 'model': model})
|
||||
content, hashtags, model = social_publisher.generate_content(post_type, context, tone=tone)
|
||||
return jsonify({'success': True, 'content': content, 'hashtags': hashtags, 'model': model})
|
||||
except Exception as e:
|
||||
logger.error(f"AI generation failed: {e}")
|
||||
return jsonify({'success': False, 'error': 'Nie udalo sie wygenerowac tresci. Sprobuj ponownie lub wpisz tresc recznie.'}), 500
|
||||
|
||||
@ -8,6 +8,7 @@ Supports per-company Facebook configuration via OAuth tokens.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, List, Tuple
|
||||
|
||||
@ -39,12 +40,12 @@ Post powinien:
|
||||
- Opisać czym zajmuje się firma (2-3 zdania)
|
||||
- Podkreślić wartość tej firmy dla społeczności biznesowej
|
||||
- Zachęcić do odwiedzenia strony firmy
|
||||
- Zawierać 3-5 hashtagów (#NordaBiznes #IzbaGospodarcza #Wejherowo + branżowe)
|
||||
- Być napisany ciepło, z dumą i wspierająco
|
||||
- Mieć 100-200 słów
|
||||
- Być po polsku
|
||||
- NIE dodawaj hashtagów — zostaną wygenerowane osobno
|
||||
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu (z hashtagami na końcu).""",
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu, BEZ hashtagów.""",
|
||||
|
||||
'event_invitation': """Napisz post na Facebook zapraszający na wydarzenie Izby Gospodarczej NORDA Biznes:
|
||||
|
||||
@ -58,11 +59,11 @@ Post powinien:
|
||||
- Zawierać najważniejsze informacje (co, kiedy, gdzie)
|
||||
- Wspomnieć korzyści z udziału
|
||||
- Zawierać CTA (zachęta do rejestracji/kontaktu)
|
||||
- Zawierać 3-5 hashtagów (#NordaBiznes #Networking #Wejherowo + tematyczne)
|
||||
- Mieć 100-150 słów
|
||||
- Być po polsku
|
||||
- NIE dodawaj hashtagów — zostaną wygenerowane osobno
|
||||
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu.""",
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu, BEZ hashtagów.""",
|
||||
|
||||
'event_recap': """Napisz post na Facebook będący relacją z wydarzenia Izby Gospodarczej NORDA Biznes:
|
||||
|
||||
@ -77,11 +78,11 @@ Post powinien:
|
||||
- Podsumować najważniejsze tematy/wnioski
|
||||
- Wspomnieć atmosferę i wartość spotkania
|
||||
- Zachęcić do udziału w kolejnych wydarzeniach
|
||||
- Zawierać 3-5 hashtagów
|
||||
- Mieć 100-200 słów
|
||||
- Być po polsku, relacyjny i dziękujący
|
||||
- NIE dodawaj hashtagów — zostaną wygenerowane osobno
|
||||
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu.""",
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu, BEZ hashtagów.""",
|
||||
|
||||
'regional_news': """Napisz post na Facebook dla Izby Gospodarczej NORDA Biznes komentujący aktualność regionalną:
|
||||
|
||||
@ -93,11 +94,11 @@ Post powinien:
|
||||
- Informować o temacie w kontekście biznesowym regionu
|
||||
- Być informacyjny i ekspercki
|
||||
- Dodać perspektywę Izby (jak to wpływa na lokalny biznes)
|
||||
- Zawierać 3-5 hashtagów (#NordaBiznes #Pomorze #Wejherowo + tematyczne)
|
||||
- Mieć 100-200 słów
|
||||
- Być po polsku
|
||||
- NIE dodawaj hashtagów — zostaną wygenerowane osobno
|
||||
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu.""",
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu, BEZ hashtagów.""",
|
||||
|
||||
'chamber_news': """Napisz post na Facebook z aktualnościami Izby Gospodarczej NORDA Biznes:
|
||||
|
||||
@ -108,11 +109,11 @@ Post powinien:
|
||||
- Być oficjalny ale przystępny
|
||||
- Przekazać najważniejsze informacje
|
||||
- Zachęcić do interakcji (pytania, komentarze)
|
||||
- Zawierać 3-5 hashtagów (#NordaBiznes #IzbaGospodarcza + tematyczne)
|
||||
- Mieć 80-150 słów
|
||||
- Być po polsku
|
||||
- NIE dodawaj hashtagów — zostaną wygenerowane osobno
|
||||
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu.""",
|
||||
Odpowiedz WYŁĄCZNIE tekstem postu, BEZ hashtagów.""",
|
||||
}
|
||||
|
||||
|
||||
@ -411,7 +412,47 @@ class SocialPublisherService:
|
||||
|
||||
# ---- AI Content Generation ----
|
||||
|
||||
def generate_content(self, post_type: str, context: dict, tone: str = None) -> Tuple[str, str]:
|
||||
@staticmethod
|
||||
def _split_hashtags(text: str) -> Tuple[str, str]:
|
||||
"""Split content and hashtags. Returns (clean_content, hashtags)."""
|
||||
lines = text.strip().split('\n')
|
||||
content_lines = []
|
||||
hashtag_words = []
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
# Line is purely hashtags (all words start with #)
|
||||
if stripped and all(w.startswith('#') for w in stripped.split()):
|
||||
hashtag_words.extend(stripped.split())
|
||||
else:
|
||||
content_lines.append(line)
|
||||
|
||||
# Also extract inline hashtags from the last content line
|
||||
if content_lines:
|
||||
last = content_lines[-1].strip()
|
||||
inline_tags = re.findall(r'#\w+', last)
|
||||
if inline_tags and len(inline_tags) >= 2:
|
||||
# Remove inline hashtags from last line
|
||||
cleaned_last = re.sub(r'\s*#\w+', '', last).strip()
|
||||
if cleaned_last:
|
||||
content_lines[-1] = cleaned_last
|
||||
else:
|
||||
content_lines.pop()
|
||||
hashtag_words.extend(inline_tags)
|
||||
|
||||
content = '\n'.join(content_lines).strip()
|
||||
# Deduplicate hashtags preserving order
|
||||
seen = set()
|
||||
unique_tags = []
|
||||
for tag in hashtag_words:
|
||||
low = tag.lower()
|
||||
if low not in seen:
|
||||
seen.add(low)
|
||||
unique_tags.append(tag)
|
||||
|
||||
return content, ' '.join(unique_tags)
|
||||
|
||||
def generate_content(self, post_type: str, context: dict, tone: str = None) -> Tuple[str, str, str]:
|
||||
"""Generate post content using AI.
|
||||
|
||||
Args:
|
||||
@ -420,7 +461,7 @@ class SocialPublisherService:
|
||||
tone: One of POST_TONES keys (default: DEFAULT_TONE)
|
||||
|
||||
Returns:
|
||||
(content: str, ai_model: str)
|
||||
(content: str, hashtags: str, ai_model: str)
|
||||
"""
|
||||
template = AI_PROMPTS.get(post_type)
|
||||
if not template:
|
||||
@ -444,7 +485,9 @@ class SocialPublisherService:
|
||||
if not result:
|
||||
raise RuntimeError("AI nie wygenerował treści. Spróbuj ponownie.")
|
||||
|
||||
return result.strip(), 'gemini-3-flash'
|
||||
# Split out any hashtags AI may have included despite instructions
|
||||
content, hashtags = self._split_hashtags(result)
|
||||
return content, hashtags, 'gemini-3-flash'
|
||||
|
||||
def generate_hashtags(self, content: str, post_type: str = '') -> Tuple[str, str]:
|
||||
"""Generate hashtags for given post content using AI.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user