Commit Graph

9 Commits

Author SHA1 Message Date
Michele
5870b5eede fix: strip markdown code fences from LLM JSON responses
Claude wraps JSON in ```json ... ``` fences even when instructed to
return raw JSON. This caused all TopicResult validations to fail with
"Invalid JSON at line 1 column 1". Strip fences before parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:10:39 +01:00
Michele
9e7205eca2 feat(04-01): integrazione Unsplash in pipeline + CSVBuilder + export
- CSVBuilder.build_csv() e build_csv_content() accettano image_url_map opzionale
- _resolve_image() risolve keyword->URL Unsplash con fallback keyword originale
- _build_rows() chiama _resolve_image per cover, slides e cta image keywords
- JobStatus ha campo image_url_map con persistenza su disco JSON
- GenerationPipeline._resolve_unsplash_keywords() chiamato dopo batch LLM
- Carica unsplash_api_key da settings.json, crea UnsplashService, chiama resolve_keywords
- image_url_map salvato nel job JSON per riuso in export con edits
- Export router recupera image_url_map dal job JSON e passa a build_csv_content
- generate_single NON risolve Unsplash (velocità e riuso map job originale)
2026-03-09 08:10:06 +01:00
Michele
afba4c5e9e feat(04-01): UnsplashService + Settings unsplash_api_key
- Crea UnsplashService con search, cache disco, traduzione IT->EN
- ~30 keyword B2B italiane tradotte in dizionario statico
- Cache in-memory + persistenza su disco (unsplash_cache.json)
- Retry automatico su errori di rete, no-retry su 401/403
- Rate limiting awareness via X-Ratelimit-Remaining header
- Aggiunge campo unsplash_api_key a Settings schema
- Router settings espone unsplash_api_key_masked + configured
- Merge None-preserving per unsplash_api_key nel PUT
2026-03-09 08:07:06 +01:00
Michele
67769dd68d feat(03-02): topic_overrides in CalendarRequest + pipeline wiring
- Aggiunto campo topic_overrides: Optional[dict[int, str]] a CalendarRequest
- GenerationPipeline._run_generation ora controlla request.topic_overrides
  prima di chiamare LLM per generare il topic
- Slot con override saltano la chiamata LLM per il topic
- Log informativo quando un override viene applicato
- Slot senza override continuano a funzionare come prima
2026-03-09 00:30:47 +01:00
Michele
d64c7f4524 feat(03-01): SwipeService CRUD + Pydantic schemas + FastAPI router
- backend/schemas/swipe.py: SwipeItem, SwipeItemCreate, SwipeItemUpdate, SwipeListResponse
- backend/services/swipe_service.py: SwipeService con load/save/add/update/delete/mark_used su swipe_file.json
- backend/routers/swipe.py: 5 endpoint REST (GET/POST/PUT/DELETE/mark-used), lazy init pattern
- backend/main.py: registra swipe.router prima del SPA catch-all mount
2026-03-09 00:22:12 +01:00
Michele
d8e3eb9415 fix(01): calendar slot merge in OutputReview for PN/Schwartz badges
GenerateResponse now includes calendar field from backend.
OutputReview merges CalendarSlot into PostResult via slot_index,
enabling BadgePN, BadgeSchwartz rendering and Retry button.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 03:14:07 +01:00
Michele
083621afd3 feat(01-03): LLMService, CSVBuilder, GenerationPipeline
- LLMService: retry 3x, RateLimitError legge retry-after header, 5xx backoff esponenziale, ValidationError riprova con istruzione correttiva, inter_request_delay 2s
- LLMService.generate_topic(): usa TopicResult come response_schema (passa per loop retry/validation)
- CSVBuilder: encoding utf-8-sig, header CANVA_FIELDS locked, mappa GeneratedPost+CalendarSlot -> 33 colonne
- GenerationPipeline: background task asyncio.create_task, _jobs dict progresso real-time, per-item try/except individuale, persistenza JSON su disco
2026-03-08 02:10:28 +01:00
Michele
ef9b9471fc feat(01-02): CalendarService, PromptService, 7 prompt .txt in italiano
- backend/services/calendar_service.py: genera 13 slot con distribuzione PN (4v+2s+2n+3r+1c+1p) e Schwartz (L5=3,L4=3,L3=4,L2=2,L1=1), ordina per funnel, ruota nicchie, calcola date
- backend/services/prompt_service.py: carica/compila/elenca prompt {{variabile}}, ValueError per variabili mancanti
- backend/data/prompts/system_prompt.txt: sistema prompt esperto content marketing B2B italiano
- backend/data/prompts/topic_generator.txt: generazione topic per slot calendario
- backend/data/prompts/pas_valore.txt: formato PAS per post valore educativo
- backend/data/prompts/listicle_valore.txt: formato Listicle per post valore
- backend/data/prompts/bab_storytelling.txt: formato BAB per post storytelling
- backend/data/prompts/aida_promozione.txt: formato AIDA per post promozionale
- backend/data/prompts/dato_news.txt: formato Dato+Implicazione per post news
2026-03-08 02:00:00 +01:00
Michele
209d8962f7 feat(01-02): costanti dominio, schemas Pydantic, FormatSelector
- backend/constants.py: CANVA_FIELDS (33 col locked), PERSUASION_DISTRIBUTION (13), SCHWARTZ_DISTRIBUTION (13)
- backend/schemas/calendar.py: CalendarSlot, CalendarRequest, CalendarResponse
- backend/schemas/generate.py: SlideContent, GeneratedPost, TopicResult, GenerateRequest, PostResult, GenerateResponse
- backend/data/format_mapping.json: matrice 6 tipi x 5 livelli (30 combinazioni)
- backend/services/format_selector.py: FormatSelector con select_format e fallback PAS
- fix .gitignore: backend/data/prompts/ e format_mapping.json non erano ignorabili
2026-03-08 01:54:58 +01:00