import type { ChatMember, ChatMessage, ImageAttachment, ImageSource, Persona } from "../../types.js"; import { formatChatMembers } from "./format-chat-members.js"; import { formatChatMessages } from "./format-chat-messages.js"; export interface Question { chatId: number; chatTitle: string; /** Как зовут спрашивающего: имя, фамилия и ник. */ askerName: string; /** Может быть пустым, если прислали только картинку. */ text: string; /** Сообщение, на которое ответил спрашивающий, — сразу передаём его как контекст. */ repliedTo?: ChatMessage; /** Участники, упомянутые в вопросе через @ник или по имени-ссылке. */ mentionedMembers: ChatMember[]; /** Упомянутые ники, которых бот в чате не встречал. */ unknownUsernames: string[]; /** Где в вопросе есть картинки — даже если модели их не отправляем. */ imageSources: ImageSource[]; /** Скачанные картинки для модели; пусто, если модель их не видит или скачать не вышло. */ images: ImageAttachment[]; } const IMAGE_SOURCES: Record = { question: "из вопроса", replied: "из сообщения, на которое ответили", }; /** Подсказка модели, откуда картинки — или что посмотреть их она не может. */ const imagesNote = ({ imageSources, images }: Question): string | undefined => { if (imageSources.length === 0) return undefined; if (images.length === 0) 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)}` : ""; const unknown = question.unknownUsernames.length > 0 ? `Эти ники в чате не встречались: ${question.unknownUsernames.map((u) => `@${u}`).join(", ")}.` : ""; return [known, unknown].filter(Boolean).join("\n") || undefined; }; /** Системные правила бота из `prompts/system.md` + личность + контекст конкретного вопроса. */ export const buildInstructions = (systemRules: string, question: Question, persona: Persona): string => [ `Твоя личность: ${persona.name}.\n${persona.prompt}`, systemRules, `Чат: «${question.chatTitle}». Вопрос задаёт ${question.askerName}.`, mentionsSection(question), ] .filter(Boolean) .join("\n\n"); export const buildPrompt = (question: Question): string => { const replied = question.repliedTo ? `Вопрос задан в ответ на сообщение:\n${formatChatMessages([question.repliedTo])}` : undefined; const asked = question.text || "(текста нет — прислали только изображение, опиши и прокомментируй его)"; return [replied, imagesNote(question), replied ? `Вопрос: ${asked}` : asked].filter(Boolean).join("\n\n"); }; /** Текст реплики пользователя — с именем, потому что в чате боту пишут разные админы. */ export const buildUserText = (question: Question): string => `${question.askerName}:\n${buildPrompt(question)}`;