import axios from "axios"; import config from "../config"; import LogLevels from "../enums/log-levels"; export default class TagGenerator { private readonly _name: string; private readonly _ollamaUrl: string; private readonly _ollamaModel: string; private readonly _ollamaTimeout: number; private readonly _enabled: boolean; constructor(name: string) { this._name = name; this._ollamaUrl = config.OLLAMA_URL; this._ollamaModel = config.OLLAMA_MODEL; this._ollamaTimeout = config.OLLAMA_TIMEOUT; this._enabled = config.OLLAMA_ENABLED; } private buildPrompt(title: string, content: string): string { return `A partir del siguiente título y contenido de una noticia de un medio digital chileno, sugiere como máximo 5 hashtags relevantes y concisos para publicar en Mastodon. Pueden ser menos de 3 si no aplica más. Reglas: - Responde ÚNICAMENTE con un array JSON de strings. - Los hashtags deben estar en español. - Sin espacios ni caracteres especiales dentro de cada hashtag. - Sin símbolo #. - Solo el array JSON, sin explicaciones. Título: ${title} Contenido: ${content} Respuesta:`; } private cleanTag(tag: string): string { return tag .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .replace(/[^a-zA-Z0-9]/g, "") .trim() .toLowerCase(); } private parseResponse(responseText: string): string[] { const cleaned = responseText.trim(); try { const parsed = JSON.parse(cleaned); if (Array.isArray(parsed)) { return parsed .slice(0, 5) .map((tag: unknown) => this.cleanTag(String(tag))) .filter((tag) => tag !== ""); } } catch { // Intento extraer el primer array JSON que aparezca en la respuesta const match = cleaned.match(/\[[\s\S]*?\]/); if (match) { return this.parseResponse(match[0]); } } return []; } public async generate(title: string, content: string): Promise { if (!this._enabled) { return []; } const trimmedTitle = title.trim(); const trimmedContent = content.trim(); if (trimmedTitle === "" && trimmedContent === "") { return []; } try { const response = await axios.post( this._ollamaUrl, { model: this._ollamaModel, prompt: this.buildPrompt(trimmedTitle, trimmedContent), stream: false }, { timeout: this._ollamaTimeout, headers: { "Content-Type": "application/json" } } ); const responseText = response.data?.response ?? ""; const tags = this.parseResponse(responseText); if (config.LOG_LEVEL === LogLevels.DEBUG) { console.debug(`${this._name} | Tags generadas por Ollama`, tags); } return tags; } catch (err: any) { console.log(`${this._name} | No se pudieron generar tags con Ollama`); console.error(err.message); return []; } } }