| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- 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<string[]> {
- 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 [];
- }
- }
- }
|