{"version":3,"file":"utils/slug-generator.mjs","sources":["webpack://@agent-tars/server/./src/utils/slug-generator.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { IAgent } from '@agent-tars/interface';\n\n/**\n * Response schema for LLM-generated slug\n */\ninterface SlugResponse {\n  /**\n   * Generated slug containing 3-5 words separated by hyphens\n   */\n  slug: string;\n}\n\n/**\n * SlugGenerator - Intelligent slug generation using LLM JSON mode\n *\n * This class provides AI-powered slug generation that can handle multilingual content\n * and produce semantic, URL-friendly slugs. It uses the LLM's JSON mode to ensure\n * structured output and proper formatting.\n *\n * Key features:\n * - Multilingual support (Chinese, English, etc.)\n * - Semantic understanding of content\n * - Consistent 3-5 word length\n * - URL-safe formatting\n * - Internal fallback to manual normalization if LLM fails\n */\nexport class SlugGenerator {\n  constructor(private agent: IAgent) {}\n\n  /**\n   * Generate a semantic slug from user message\n   * Handles all normalization logic internally, no external fallback needed\n   *\n   * @param userMessage The original user message to generate slug from\n   * @returns Promise resolving to a normalized slug string\n   */\n  async generateSlug(userMessage: string): Promise<string> {\n    if (!userMessage.trim()) {\n      return this.getDefaultSlug();\n    }\n\n    try {\n      // Try LLM-powered generation first\n      const llmSlug = await this.generateWithLLM(userMessage);\n      if (llmSlug) {\n        return llmSlug;\n      }\n    } catch (error) {\n      console.warn('LLM slug generation failed, using manual normalization:', error);\n    }\n\n    // Fallback to manual normalization\n    return this.manualNormalization(userMessage);\n  }\n\n  // /multimodal/agent-tars-server/src/utils/slug-generator.ts\n  /**\n   * Generate slug using LLM JSON mode\n   */\n  private async generateWithLLM(userMessage: string): Promise<string | null> {\n    const response = await this.agent.callLLM({\n      messages: [\n        {\n          role: 'system',\n          content: `You are a URL slug generator. Generate a semantic, URL-friendly slug from the given text.\n\nRequirements:\n- Use 3-5 words separated by hyphens\n- Use only lowercase English words\n- No special characters except hyphens\n- Capture the main topic/intent of the text\n- Handle multilingual input (Chinese, English, etc.)\n- NEVER include non-ASCII characters like Chinese in the output\n\nReturn only a JSON object with a \"slug\" field.`,\n        },\n        {\n          role: 'user',\n          content: `Generate a slug for: \"${userMessage}\"`,\n        },\n      ],\n      response_format: { type: 'json_object' },\n      temperature: 0.3,\n      max_tokens: 100,\n    });\n\n    const content = response.choices[0]?.message?.content;\n    if (!content) {\n      return null;\n    }\n\n    try {\n      const parsed = JSON.parse(content) as SlugResponse;\n\n      // Apply manual normalization to ensure LLM output is also sanitized\n      return this.manualNormalization(parsed.slug);\n    } catch (error) {\n      console.error('Failed to parse LLM slug response:', error);\n      return null;\n    }\n  }\n\n  /**\n   * Manual normalization - the consolidated logic from all places\n   */\n  private manualNormalization(text: string): string {\n    // First, attempt to transliterate non-ASCII characters\n    // Then apply standard normalization\n    const normalized = text\n      .toLowerCase()\n      // First, remove all non-ASCII characters completely\n      .replace(/[^\\x00-\\x7F]+/g, '-')\n      .replace(/[^\\w\\s-]/g, '') // Remove remaining special characters\n      .replace(/\\s+/g, '-') // Replace spaces with hyphens\n      .replace(/-+/g, '-') // Remove consecutive hyphens\n      .substring(0, 60) // Limit length\n      .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens\n\n    if (!normalized || normalized.length === 0) {\n      return this.getDefaultSlug();\n    }\n\n    // Take first few words if too long\n    const words = normalized.split('-').filter((word) => word.length > 0);\n    return words.slice(0, 4).join('-') || this.getDefaultSlug();\n  }\n\n  /**\n   * Get default slug when all else fails\n   */\n  private getDefaultSlug(): string {\n    return 'untitled-session';\n  }\n}\n"],"names":["SlugGenerator","userMessage","llmSlug","error","console","_response_choices__message","response","content","parsed","JSON","text","normalized","words","word","agent"],"mappings":";;;;AAGC;;;;;;;;;;AA4BM,MAAMA;IAUX,MAAM,aAAaC,WAAmB,EAAmB;QACvD,IAAI,CAACA,YAAY,IAAI,IACnB,OAAO,IAAI,CAAC,cAAc;QAG5B,IAAI;YAEF,MAAMC,UAAU,MAAM,IAAI,CAAC,eAAe,CAACD;YAC3C,IAAIC,SACF,OAAOA;QAEX,EAAE,OAAOC,OAAO;YACdC,QAAQ,IAAI,CAAC,2DAA2DD;QAC1E;QAGA,OAAO,IAAI,CAAC,mBAAmB,CAACF;IAClC;IAMA,MAAc,gBAAgBA,WAAmB,EAA0B;YA2BzDI,4BAAAA;QA1BhB,MAAMC,WAAW,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;YACxC,UAAU;gBACR;oBACE,MAAM;oBACN,SAAS,CAAC;;;;;;;;;;8CAU0B,CAAC;gBACvC;gBACA;oBACE,MAAM;oBACN,SAAS,CAAC,sBAAsB,EAAEL,YAAY,CAAC,CAAC;gBAClD;aACD;YACD,iBAAiB;gBAAE,MAAM;YAAc;YACvC,aAAa;YACb,YAAY;QACd;QAEA,MAAMM,UAAU,QAAAF,CAAAA,qBAAAA,SAAS,OAAO,CAAC,EAAE,AAAD,IAAlBA,KAAAA,IAAAA,QAAAA,CAAAA,6BAAAA,mBAAqB,OAAO,AAAD,IAA3BA,KAAAA,IAAAA,2BAA8B,OAAO;QACrD,IAAI,CAACE,SACH,OAAO;QAGT,IAAI;YACF,MAAMC,SAASC,KAAK,KAAK,CAACF;YAG1B,OAAO,IAAI,CAAC,mBAAmB,CAACC,OAAO,IAAI;QAC7C,EAAE,OAAOL,OAAO;YACdC,QAAQ,KAAK,CAAC,sCAAsCD;YACpD,OAAO;QACT;IACF;IAKQ,oBAAoBO,IAAY,EAAU;QAGhD,MAAMC,aAAaD,KAChB,WAAW,GAEX,OAAO,CAAC,kBAAkB,KAC1B,OAAO,CAAC,aAAa,IACrB,OAAO,CAAC,QAAQ,KAChB,OAAO,CAAC,OAAO,KACf,SAAS,CAAC,GAAG,IACb,OAAO,CAAC,YAAY;QAEvB,IAAI,CAACC,cAAcA,AAAsB,MAAtBA,WAAW,MAAM,EAClC,OAAO,IAAI,CAAC,cAAc;QAI5B,MAAMC,QAAQD,WAAW,KAAK,CAAC,KAAK,MAAM,CAAC,CAACE,OAASA,KAAK,MAAM,GAAG;QACnE,OAAOD,MAAM,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,cAAc;IAC3D;IAKQ,iBAAyB;QAC/B,OAAO;IACT;IAzGA,YAAoBE,KAAa,CAAE;;aAAfA,KAAK,GAALA;IAAgB;AA0GtC"}