ai: implement ollama rewrite provider + ryan-salazar byline default

The press-release rewriter (ai_rewrite_jobs) was stuck because:
- BB_REWRITE_PROVIDER defaulted to "anthropic" in code, AND
- production env still had BB_REWRITE_PROVIDER=anthropic, AND
- ollamaProvider.rewrite() was a stub that threw "not implemented".
Every job since 2026-01-26 hit Anthropic 400 "credit balance too
low" — 12 failed_api in a row.

Changes:
- Implement ollamaProvider against /v1/chat/completions on the local
  Ollama (works against AI_001:11434 / llama3.1:70b). Same JSON
  contract as the Anthropic provider; gets prompt_tokens /
  completion_tokens out of Ollama's usage block.
- Flip the default provider from "anthropic" → "ollama" in
  getProvider(), so the rewriter works out of the box now that the
  Ollama implementation exists.
- Make the BB auto-story byline configurable via env. Default flips
  from a 12-name pen rotation to a single "Ryan Salazar" byline for
  Broadcast Beat (per editorial directive). AV Beat keeps its own
  pen pool. Set BB_AUTOSTORY_BYLINE=rotate to restore the old
  behaviour.

Production env still needs (set in Coolify dashboard):
  OLLAMA_ENDPOINT=http://10.10.0.66:11434
  OLLAMA_MODEL_DEFAULT=llama3.1:70b
  BB_REWRITE_PROVIDER=ollama
  BB_REWRITE_MODEL=llama3.1:70b
  (remove ANTHROPIC_API_KEY)
This commit is contained in:
Ryan Salazar
2026-05-20 06:42:22 +00:00
parent f7c462891f
commit 80b307ba92
2 changed files with 131 additions and 10 deletions

View File

@@ -3,19 +3,33 @@ import { createClient } from '@/lib/supabase/server';
import { createAdminClient } from '@/lib/supabase/admin';
import { hybridAI } from '@/lib/ai/hybridRouter';
// ─── Pen name rotation ────────────────────────────────────────────────────────
const BB_PEN_NAMES = [
// ─── Byline ───────────────────────────────────────────────────────────────────
// All auto-generated BB stories publish under Ryan Salazar's byline (founder).
// AV Beat retains its own pen pool. To re-enable the BB rotation, set
// BB_AUTOSTORY_BYLINE="rotate" — env override so we don't need a code change
// when the editorial policy changes.
const AV_PEN_NAMES = ['Rex Chandler', 'Dana Flux', 'Derek Wainwright', 'Sloane Rigging', 'Chip Crosspoint', 'Blair Presenter', 'Jordan Lumen'];
const BB_PEN_NAMES_LEGACY = [
'Michael Strand', 'David Harlow', 'Karen Fielding', 'James Mercer',
'Peter Calloway', 'Sandra Voss', 'Brian Kowalski', 'Laura Pennington',
'Thomas Reeves', 'Christine Vale', 'Marcus Webb', 'Ellen Forsythe',
];
const AV_PEN_NAMES = ['Rex Chandler', 'Dana Flux', 'Derek Wainwright', 'Sloane Rigging', 'Chip Crosspoint', 'Blair Presenter', 'Jordan Lumen'];
const BB_AUTOSTORY_BYLINE = process.env.BB_AUTOSTORY_BYLINE || 'Ryan Salazar';
function pickPenName(siteId: number, recentNames: string[]): string {
const pool = siteId === 2 ? AV_PEN_NAMES : BB_PEN_NAMES;
// Avoid last 3 used names
const available = pool.filter(n => !recentNames.slice(-3).includes(n));
return available[Math.floor(Math.random() * available.length)] || pool[0];
// AV Beat: rotate its small pen pool
if (siteId === 2) {
const pool = AV_PEN_NAMES;
const available = pool.filter(n => !recentNames.slice(-3).includes(n));
return available[Math.floor(Math.random() * available.length)] || pool[0];
}
// Broadcast Beat: fixed byline (Ryan) unless overridden to "rotate".
if (BB_AUTOSTORY_BYLINE.toLowerCase() === 'rotate') {
const pool = BB_PEN_NAMES_LEGACY;
const available = pool.filter(n => !recentNames.slice(-3).includes(n));
return available[Math.floor(Math.random() * available.length)] || pool[0];
}
return BB_AUTOSTORY_BYLINE;
}
// ─── Company extraction via Hybrid AI ────────────────────────────────────────