Switched to Openrouter. Improves personalities descriptions.

This commit is contained in:
hbrain 2026-06-05 07:50:44 +02:00
parent a3bbc8417f
commit bb0ca103ae

View file

@ -13,14 +13,16 @@ if [ "$ADDITIONAL_PROMPT" = "no-append" ]; then
ADDITIONAL_PROMPT="" ADDITIONAL_PROMPT=""
fi fi
# Source system-wide Gemini API key if present so the webserver can run this as www-data. # Source system-wide OpenRouter API key if present so the webserver can run this as www-data.
if [ -f /etc/mvlog/gemini.env ]; then if [ -f /etc/mvlog/openrouter.env ]; then
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. /etc/mvlog/gemini.env . /etc/mvlog/openrouter.env
fi fi
: "${GEMINI_API_KEY:?Set GEMINI_API_KEY first in the environment}" : "${OPENROUTER_API_KEY:?Set OPENROUTER_API_KEY first in the environment}"
MODEL="${GEMINI_MODEL:-gemini-2.5-flash}" 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")" WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/mvlog.XXXXXX")"
FRAMES_DIR="$WORKDIR/frames" FRAMES_DIR="$WORKDIR/frames"
@ -131,15 +133,15 @@ EOF
fi fi
PERSONALITIES=( 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[@]} 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!): Use the following personality and write entirely in that style (but never mention your name!):
"$PERSONALITY" "$PERSONALITY"
${ADDITIONAL_PROMPT_BLOCK} ${ADDITIONAL_PROMPT_BLOCK}
Then create a teaser in English. Then create a teaser in English.
The teaser must be max. 32 words. 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. Do not simply summarize the trip.
Highlight the funniest, strangest, most beautiful, or most ridiculous aspect. 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. Then create 5 to 8 lowercase tags relevant to the trip.
Use short tags only, for example: motorcycle, roadtrip, weather, landscape, ferry, town, mountains. Use short tags only, for example: motorcycle, roadtrip, weather, landscape, ferry, town, mountains.
@ -194,7 +197,7 @@ Location: "$LOCATION"
JSON format: JSON format:
{ {
"personality": "shoresy | marvin | churchill | house | blackadder", "personality": "shoresy | marvin | churchill | blackadder",
"quote_da": "Danish quoted sentence here", "quote_da": "Danish quoted sentence here",
"teaser": "English teaser here", "teaser": "English teaser here",
"description": "English travel blog description here", "description": "English travel blog description here",
@ -216,7 +219,7 @@ RESPONSE_FILE="$WORKDIR/response.json"
printf '%s' "$PROMPT" > "$PROMPT_FILE" printf '%s' "$PROMPT" > "$PROMPT_FILE"
printf '%s\n' "${FRAMES[@]}" > "$FRAMES_LIST" 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 sys
import json import json
import base64 import base64
@ -224,11 +227,12 @@ import base64
prompt_file = sys.argv[1] prompt_file = sys.argv[1]
request_file = sys.argv[2] request_file = sys.argv[2]
frames_list = sys.argv[3] frames_list = sys.argv[3]
model = sys.argv[4]
with open(prompt_file, "r", encoding="utf-8") as f: with open(prompt_file, "r", encoding="utf-8") as f:
prompt = f.read() prompt = f.read()
parts = [{"text": prompt}] content = [{"type": "text", "text": prompt}]
with open(frames_list, "r", encoding="utf-8") as fl: with open(frames_list, "r", encoding="utf-8") as fl:
for line in 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: with open(img, "rb") as fh:
b64 = base64.b64encode(fh.read()).decode("ascii") b64 = base64.b64encode(fh.read()).decode("ascii")
parts.append({ content.append({
"inline_data": { "type": "image_url",
"mime_type": "image/jpeg", "image_url": {
"data": b64 "url": f"data:image/jpeg;base64,{b64}"
} }
}) })
req = { req = {
"contents": [ "model": model,
"messages": [
{ {
"parts": parts "role": "user",
"content": content
} }
], ],
"generationConfig": {
"temperature": 0.7, "temperature": 0.7,
"responseMimeType": "application/json" "response_format": {
"type": "json_object"
} }
} }
with open(request_file, "w", encoding="utf-8") as rf: with open(request_file, "w", encoding="utf-8") as rf:
json.dump(req, rf, ensure_ascii=False) json.dump(req, rf, ensure_ascii=False)
PY PY
echo "Calling OpenRouter API..." >&2
echo "Calling Gemini API..." >&2
echo "DEBUG_MODEL=$MODEL" >&2 echo "DEBUG_MODEL=$MODEL" >&2
echo "DEBUG_PERSONALITY_INDEX=$idx" >&2 echo "DEBUG_PERSONALITY_INDEX=$idx" >&2
curl -sS \ 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 "Content-Type: application/json" \
-H "HTTP-Referer: ${OPENROUTER_SITE_URL}" \
-H "X-Title: ${OPENROUTER_SITE_NAME}" \
-X POST \ -X POST \
-d @"$REQUEST_FILE" \ -d @"$REQUEST_FILE" \
> "$RESPONSE_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 if [ -z "$TEXT" ]; then
echo "Gemini returned no text. Full response:" >&2 echo "OpenRouter returned no text. Full response:" >&2
cat "$RESPONSE_FILE" >&2 cat "$RESPONSE_FILE" >&2
exit 1 exit 1
fi fi
@ -286,7 +294,7 @@ JOBNAME="$(basename "$IN_DIR")"
OUT_JSON="$OUT_PARENT/${JOBNAME}.json" OUT_JSON="$OUT_PARENT/${JOBNAME}.json"
if ! echo "$TEXT" | jq . > "$OUT_JSON"; then 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 echo "$TEXT" >&2
exit 1 exit 1
fi fi
@ -294,6 +302,6 @@ fi
chown www-data:www-data "$OUT_JSON" 2>/dev/null || true chown www-data:www-data "$OUT_JSON" 2>/dev/null || true
chmod 664 "$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 exit 0