havre: a photo or clip they sent is told to the model in their own turn, as a stage direction, not in yours (characters took "you sent me a photo" as yours and got confused); a reply that narrates sending a photo makes one likely

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAvUH72XsdCitHdh3t7mjr
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-20 05:37:15 +00:00
1 parent ba441f8d2d
commit c7d6f427f8
1 file changed
+9 -7
+9 -7
View File
@@ -270,25 +270,27 @@ export function clipOptions(model, last = {}) {
/**
* A conversation with pictures and voice notes in it, as plain messages for a text model. A photo or clip the
* character sent is mentioned in the next thing the person says ("(You sent me a photo: …)"), so the model knows
* about it without learning to write "[photo]" itself. Your photo is "[a photo: what it shows]", your voice note is
* the words you said.
* character sent is in their own turn, as a stage direction ("*sends a photo: …*"), the way characters narrate what
* they do, so the model knows what it sent and who sent it (told in the person's turn, "you sent me a photo" read as
* the person's, and characters got confused about whose photo it was). Your photo is "[a photo: what it shows]", your
* voice note is the words you said.
*/
export function apiHistory(history) {
const out = [];
let notes = [];
for (const m of history || []) {
if (!m || m.demo) continue;
const media = m.media || null;
if (m.role === "assistant") {
if (media && (media.kind === "photo" || media.kind === "clip")) {
const shot = clip(str(media.shot).trim(), 300).replace(/[.!?…\s]+$/, "");
notes.push(`(You sent me ${media.kind === "clip" ? "a short video" : "a photo"}${shot ? ": " + shot : ""}.)`);
const sent = `*sends ${media.kind === "clip" ? "a short video" : "a photo"}${shot ? ": " + shot : ""}*`;
const last = out.at(-1);
if (last && last.role === "assistant") last.content += "\n\n" + sent; // with the words it came after
else out.push({ role: "assistant", content: sent });
} else if (str(m.content).trim()) out.push({ role: "assistant", content: str(m.content) });
} else if (m.role === "user") {
let content = str(m.content);
if (media && media.kind === "photo-in") content = `[a photo${media.saw ? ": " + media.saw : ""}]` + (content ? " " + content : "");
if (notes.length) { content = notes.join(" ") + (content ? "\n\n" + content : ""); notes = []; }
out.push({ role: "user", content, ...(m.hidden ? { hidden: true } : {}) });
}
}
@@ -357,7 +359,7 @@ export function normalizeMove(o) {
// words that make a move likely: asking to see or hear them, or a reply about where they are and what they're doing
const ASKS = /\b(pics?|picture|photo|selfie|snap|show me|see you|see it|what (?:do )?you look like|look like|wearing|outfit|send (?:me )?(?:a|one|it)|video|clip|film|hear you|your voice|say it|sing|whisper|out loud|aloud)\b/i;
const SHOWS = /\b(look at (?:this|my|the)|wearing|outfit|dress|view from|sunset|sunrise|my (?:room|place|desk|garden|kitchen|car|boat|bed|window)|just got (?:back|a|my)|i'?m (?:at|in|on) the|picture|photo|selfie|whisper)\b/i;
const SHOWS = /\b(sends? (?:you )?(?:a |another )?(?:photo|pic|selfie|snap)|look at (?:this|my|the)|wearing|outfit|dress|view from|sunset|sunrise|my (?:room|place|desk|garden|kitchen|car|boat|bed|window)|just got (?:back|a|my)|i'?m (?:at|in|on) the|picture|photo|selfie|whisper)\b/i;
/**
* Whether to think about a move after this reply: when the person asked to see or hear them, when the reply talks