A marathon GygaxBot session that opened with "the AI illustrations stopped using our character reference photos" and didn't close until four layers down. Along the way: Discord message chunking, a campaign Q&A command, an images-only viewer, a never-installed dependency that had been quietly disabling Gemini on the server for weeks, a torch pin to stop rebuilds from bricking the API, and a scene extractor caught inventing characters who were never at the table.
Quality-of-Life First
Before the deep dive, a stack of paper cuts. Discord's 2000-character limit was erroring on long messages and truncating session recaps, so we added chunking helpers that split on paragraph, then line, then word boundaries, and routed every previously-truncated send through them — plus made the recap library return full content instead of pre-cutting it. The !session view audio links were bare localhost URLs Discord can't reach, so they got swapped for real uploaded attachments with a native player. Then the features: !session archive opened to all players, a new !ask that does RAG Q&A over the raw session transcripts (and answers "how do I use the bot" from a built-in command reference), and !session images for an images-only gallery.
Gemini Was Never Actually Running
The headline bug: illustrations ignored the character reference photos entirely. The first suspect was obvious — the reference images were git-ignored and never reached the server, so we synced them out-of-band. Still ignored. Ground-truth digging inside the container found the real culprit: the google-genai SDK was never in the image's requirements, so Gemini initialized as "unavailable" and the pipeline silently fell back to ComfyUI/FLUX — whose code path passes no reference images at all. It "worked locally" only because the dev box had the package pip-installed by hand. The production host had been drawing reference-less, generic art the whole time and labeling the jobs "gemini" anyway.
Dependency Hell and a Self-Inflicted Outage
Adding the SDK to requirements re-resolved the pinned ML stack and pulled torch 2.12.1, which crashes on the base image's ancient python 3.11.0rc1 (missing sys.get_int_max_str_digits) — taking the entire API down in a crash loop. Reverted and restored service in about 35 seconds. The proper fix came in two pieces, both validated in throwaway containers before the live container was ever switched: install google-genai in an isolated --no-deps step so it can't perturb torch, and pin torch to the known-good 2.11.0 so rebuilds are reproducible instead of a roll of the dice. Lesson re-learned the hard way: build the new image, prove it imports and the model loads, then recreate.
Phantom NPCs
With Gemini finally live, a character named Helga showed up in scenes she was never in — and the raw transcript didn't mention her a single time. Root cause: the scene extractor was injecting every campaign character's appearance file into the Ollama prompt with "use these when describing characters," priming the model to write absent roster members into the story, and their reference portraits dutifully followed. The fix is three layered guards: only inject a character's description if their name actually appears in the transcript (kills the priming), deterministically drop any character not in the transcript from the structured lists that drive image selection (the backstop), and harden the prompt against inventing anyone. The presence check matches on alphanumeric token concatenation, so "Crazy 88" and "Crazy88" both match the transcript — but "Pip" correctly refuses to match the word "pipes."
What's Next
- Wire FLUX + IP-adapter as a second, reference-driven illustration path
- Re-extract older sessions to scrub pre-fix phantom characters from their scene data
- Replace the base image's
3.11.0rc1Python so torch no longer has to stay pinned