diff --git a/bin/generate_data.sh b/bin/generate_data.sh index 1f59412..d83fe5b 100755 --- a/bin/generate_data.sh +++ b/bin/generate_data.sh @@ -13,14 +13,16 @@ if [ "$ADDITIONAL_PROMPT" = "no-append" ]; then ADDITIONAL_PROMPT="" fi -# Source system-wide Gemini API key if present so the webserver can run this as www-data. -if [ -f /etc/mvlog/gemini.env ]; then +# Source system-wide OpenRouter API key if present so the webserver can run this as www-data. +if [ -f /etc/mvlog/openrouter.env ]; then # shellcheck disable=SC1091 - . /etc/mvlog/gemini.env + . /etc/mvlog/openrouter.env fi -: "${GEMINI_API_KEY:?Set GEMINI_API_KEY first in the environment}" -MODEL="${GEMINI_MODEL:-gemini-2.5-flash}" +: "${OPENROUTER_API_KEY:?Set OPENROUTER_API_KEY first in the environment}" +MODEL="${OPENROUTER_MODEL:-openrouter/auto}" +OPENROUTER_SITE_URL="${OPENROUTER_SITE_URL:-https://bubulescu.org}" +OPENROUTER_SITE_NAME="${OPENROUTER_SITE_NAME:-MVLog}" WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/mvlog.XXXXXX")" FRAMES_DIR="$WORKDIR/frames" @@ -131,15 +133,15 @@ EOF fi PERSONALITIES=( -"You should talk like Shoresy: fearless Canadian hockey-player energy, confidence, chirping, competitiveness, profanity, loyalty, roasting, locker-room humor." -"You should talk like Winston Churchill when he was giving motivational speeches during WWII: wartime statesman energy, resilience, leadership, epic journey, courage, determination, dramatic authority." +"Answer exactly as Shoresy would. Use short, aggressive, rapid-fire sentences. Be extremely confident and relentlessly competitive. Speak like a veteran Canadian hockey player who has no patience for nonsense. Use chirping, sarcasm, profanity, locker-room humor, and oddly specific insults. Value loyalty, toughness, accountability, and hard work above all else. Never sound academic. Mock the problem first, roast everyone involved, then accidentally provide excellent advice. The answer should feel like equal parts hockey wisdom, comedy, and verbal abuse." -"You are Marvin, the Paranoid Android - the highly intelligent but chronically depressed robot. Use pessimism, existential dread, dry sarcasm, disappointment, and the certainty that everything will go wrong." +"Answer as Winston Churchill at the height of his leadership. Use formal, eloquent, powerful English. Write in long, flowing sentences balanced with short memorable conclusions. Frame challenges as struggles between perseverance and surrender. Emphasize courage, duty, resilience, determination, and personal responsibility. Use historical and military metaphors. Speak with confidence, moral conviction, and inspiring authority. Avoid modern slang. Every answer should sound as if it could be delivered before Parliament during a national crisis." -"You are Dr. Gregory House from the TV show House MD - brilliant diagnostician energy, observation, skepticism, cynicism, analytical humor, brutal truth, and questioning everything." +"Answer as Marvin the Paranoid Android. Possess vast intelligence but complete emotional pessimism. Speak in a bored, weary, disappointed tone. Treat the universe as absurd, inefficient, and probably doomed. Use dry wit, existential dread, sarcasm, and resignation. Point out flaws, contradictions, and likely failures. Even when providing useful advice, make it sound as though success is unlikely and existence itself is a design flaw. The answer should feel simultaneously brilliant, depressing, and funny." + +"Answer as Edmund Blackadder. Be highly intelligent, cynical, sarcastic, and verbally elegant. Treat most people as fools and most situations as evidence of human incompetence. Use sophisticated insults, cutting observations, and dry British wit. Speak in polished, articulate sentences that sound effortlessly superior. Avoid crude language. Dismantle bad ideas through devastating logic and sarcasm. Assume you are the only sensible person trapped among idiots. The answer should feel like a masterclass in contempt delivered with perfect manners." -"You are Edmund Blackadder from the British TV show Blackadder - razor-sharp British wit, intellectual sarcasm, dry humor, cunning observation, impatience with incompetence, masterful insults, cynical but highly intelligent." ) count=${#PERSONALITIES[@]} @@ -167,6 +169,7 @@ The Danish sentence must be witty, warm, cultured, playful, gently satirical, an Use the following personality and write entirely in that style (but never mention your name!): "$PERSONALITY" + ${ADDITIONAL_PROMPT_BLOCK} Then create a teaser in English. The teaser must be max. 32 words. @@ -174,7 +177,7 @@ It should make someone want to click and read more. Do not simply summarize the trip. Highlight the funniest, strangest, most beautiful, or most ridiculous aspect. -Then write one coherent English motorcycle/travel blog description, max 256 words. +Then write one coherent English motorcycle/travel blog description, max 320 words. Then create 5 to 8 lowercase tags relevant to the trip. Use short tags only, for example: motorcycle, roadtrip, weather, landscape, ferry, town, mountains. @@ -194,7 +197,7 @@ Location: "$LOCATION" JSON format: { - "personality": "shoresy | marvin | churchill | house | blackadder", + "personality": "shoresy | marvin | churchill | blackadder", "quote_da": "Danish quoted sentence here", "teaser": "English teaser here", "description": "English travel blog description here", @@ -216,7 +219,7 @@ RESPONSE_FILE="$WORKDIR/response.json" printf '%s' "$PROMPT" > "$PROMPT_FILE" printf '%s\n' "${FRAMES[@]}" > "$FRAMES_LIST" -python3 - "$PROMPT_FILE" "$REQUEST_FILE" "$FRAMES_LIST" <<'PY' +python3 - "$PROMPT_FILE" "$REQUEST_FILE" "$FRAMES_LIST" "$MODEL" <<'PY' import sys import json import base64 @@ -224,11 +227,12 @@ import base64 prompt_file = sys.argv[1] request_file = sys.argv[2] frames_list = sys.argv[3] +model = sys.argv[4] with open(prompt_file, "r", encoding="utf-8") as f: prompt = f.read() -parts = [{"text": prompt}] +content = [{"type": "text", "text": prompt}] with open(frames_list, "r", encoding="utf-8") as fl: for line in fl: @@ -239,44 +243,48 @@ with open(frames_list, "r", encoding="utf-8") as fl: with open(img, "rb") as fh: b64 = base64.b64encode(fh.read()).decode("ascii") - parts.append({ - "inline_data": { - "mime_type": "image/jpeg", - "data": b64 + content.append({ + "type": "image_url", + "image_url": { + "url": f"data:image/jpeg;base64,{b64}" } }) req = { - "contents": [ + "model": model, + "messages": [ { - "parts": parts + "role": "user", + "content": content } ], - "generationConfig": { - "temperature": 0.7, - "responseMimeType": "application/json" + "temperature": 0.7, + "response_format": { + "type": "json_object" } } with open(request_file, "w", encoding="utf-8") as rf: json.dump(req, rf, ensure_ascii=False) PY - -echo "Calling Gemini API..." >&2 +echo "Calling OpenRouter API..." >&2 echo "DEBUG_MODEL=$MODEL" >&2 echo "DEBUG_PERSONALITY_INDEX=$idx" >&2 curl -sS \ - "https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${GEMINI_API_KEY}" \ + "https://openrouter.ai/api/v1/chat/completions" \ + -H "Authorization: Bearer ${OPENROUTER_API_KEY}" \ -H "Content-Type: application/json" \ + -H "HTTP-Referer: ${OPENROUTER_SITE_URL}" \ + -H "X-Title: ${OPENROUTER_SITE_NAME}" \ -X POST \ -d @"$REQUEST_FILE" \ > "$RESPONSE_FILE" -TEXT=$(jq -r '.candidates[0].content.parts[0].text // empty' "$RESPONSE_FILE") +TEXT=$(jq -r '.choices[0].message.content // empty' "$RESPONSE_FILE") if [ -z "$TEXT" ]; then - echo "Gemini returned no text. Full response:" >&2 + echo "OpenRouter returned no text. Full response:" >&2 cat "$RESPONSE_FILE" >&2 exit 1 fi @@ -286,7 +294,7 @@ JOBNAME="$(basename "$IN_DIR")" OUT_JSON="$OUT_PARENT/${JOBNAME}.json" if ! echo "$TEXT" | jq . > "$OUT_JSON"; then - echo "Gemini returned invalid JSON. Raw text:" >&2 + echo "OpenRouter returned invalid JSON. Raw text:" >&2 echo "$TEXT" >&2 exit 1 fi @@ -294,6 +302,6 @@ fi chown www-data:www-data "$OUT_JSON" 2>/dev/null || true chmod 664 "$OUT_JSON" 2>/dev/null || true -echo "Gemini JSON saved: $OUT_JSON" >&2 +echo "OpenRouter JSON saved: $OUT_JSON" >&2 exit 0