import { isModelId, type ChatRequest, type MessageRole, type ModelId, } from "@/lib/chat/types"; import { MAX_CHAT_MESSAGE_CHARACTERS, MAX_CHAT_REQUEST_CHARACTERS, MAX_CHAT_REQUEST_MESSAGES, } from "@/lib/chat/request-limits"; const roles = new Set(["user", "assistant"]); export class ChatValidationError extends Error { readonly status = 400; constructor() { super("Некорректный запрос"); this.name = "ChatValidationError"; } } export function validateChatRequest(value: unknown): ChatRequest { if (!isRecord(value) || !isModelId(value.model) || !Array.isArray(value.messages)) { throw new ChatValidationError(); } if ( value.messages.length === 0 || value.messages.length > MAX_CHAT_REQUEST_MESSAGES ) { throw new ChatValidationError(); } let totalCharacters = 0; const messages = Array.from(value.messages, (message) => { if ( !isRecord(message) || !isMessageRole(message.role) || typeof message.content !== "string" || message.content.trim().length === 0 || message.content.length > MAX_CHAT_MESSAGE_CHARACTERS ) { throw new ChatValidationError(); } totalCharacters += message.content.length; if (totalCharacters > MAX_CHAT_REQUEST_CHARACTERS) { throw new ChatValidationError(); } return { role: message.role, content: message.content }; }); return { model: value.model, messages }; } export type PersistentChatRequest = { conversationId?: string; model: ModelId; content?: string; retry?: boolean; attachmentIds?: string[]; }; export function validatePersistentChatRequest(value: unknown): PersistentChatRequest { if (!isRecord(value) || !isModelId(value.model)) { throw new ChatValidationError(); } const conversationId = value.conversationId; const content = value.content; const retry = value.retry; const attachmentIds = value.attachmentIds; if ( (conversationId !== undefined && (typeof conversationId !== "string" || !conversationId)) || (content !== undefined && (typeof content !== "string" || !content.trim() || content.length > MAX_CHAT_MESSAGE_CHARACTERS)) || (retry !== undefined && typeof retry !== "boolean") || (attachmentIds !== undefined && (!Array.isArray(attachmentIds) || attachmentIds.length > 10 || attachmentIds.some((id) => typeof id !== "string" || !id) || new Set(attachmentIds).size !== attachmentIds.length)) || (retry === true && content !== undefined) || (retry !== true && content === undefined && (!attachmentIds || attachmentIds.length === 0)) ) { throw new ChatValidationError(); } return { conversationId, model: value.model, content, retry, attachmentIds }; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function isMessageRole(value: unknown): value is MessageRole { return typeof value === "string" && roles.has(value as MessageRole); }