Фото в вопросах, Gemini 3.8 Flash по умолчанию, команды /context и /clear

- Бот смотрит фото: подпись с упоминанием, /ask в подписи или ответом на фото, фото в личке
- Картинки скачиваются через Bot API и передаются модели байтами (ссылка с токеном наружу не уходит)
- OpenRouter по умолчанию — openrouter/google/gemini-3.8-flash; AI_IMAGE_INPUT для явного выбора
- Модель без зрения получает пометку, что фото есть, но она его не видит
- /context: сколько бот помнит и видит ли всю переписку (privacy mode / админ)
- /clear: очистка сохранённой переписки чата с подтверждением

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yunogasai
2026-09-24 20:04:06 +02:00
parent 34a8d426d2
commit f8273e8a23
15 changed files with 313 additions and 46 deletions

View File

@@ -1,4 +1,4 @@
import type { ChatMember, ChatMessage, Persona } from "../../types.js";
import type { ChatMember, ChatMessage, ImageAttachment, Persona } from "../../types.js";
import { formatChatMembers } from "./format-chat-members.js";
import { formatChatMessages } from "./format-chat-messages.js";
@@ -14,8 +14,24 @@ export interface Question {
mentionedMembers: ChatMember[];
/** Упомянутые ники, которых бот в чате не встречал. */
unknownUsernames: string[];
/** Фото из вопроса и из сообщения, на которое ответили. */
images: ImageAttachment[];
}
const IMAGE_SOURCES: Record<ImageAttachment["source"], string> = {
question: "из вопроса",
replied: "из сообщения, на которое ответили",
};
/** Подсказка модели, откуда картинки — или что посмотреть их она не может. */
const imagesNote = (images: ImageAttachment[], canSeeImages: boolean): string | undefined => {
if (images.length === 0) return undefined;
if (!canSeeImages) return "К сообщению приложено изображение, но ты не можешь его просмотреть — честно скажи об этом.";
return `Приложены изображения (по порядку): ${images.map((image) => IMAGE_SOURCES[image.source]).join(", ")}.`;
};
const mentionsSection = (question: Question): string | undefined => {
const known = question.mentionedMembers.length > 0 ? `Упомянуты участники:\n${formatChatMembers(question.mentionedMembers)}` : "";
@@ -38,7 +54,12 @@ export const buildInstructions = (systemRules: string, question: Question, perso
.filter(Boolean)
.join("\n\n");
export const buildPrompt = (question: Question): string =>
question.repliedTo
? `Вопрос задан в ответ на сообщение:\n${formatChatMessages([question.repliedTo])}\n\nВопрос: ${question.text}`
: question.text;
export const buildPrompt = (question: Question, canSeeImages: boolean): string => {
const replied = question.repliedTo
? `Вопрос задан в ответ на сообщение:\n${formatChatMessages([question.repliedTo])}`
: undefined;
const text = replied ? `Вопрос: ${question.text}` : question.text;
return [replied, imagesNote(question.images, canSeeImages), text].filter(Boolean).join("\n\n");
};