diff --git a/services/geminiService.ts b/services/geminiService.ts index 8809471..c93d3c2 100644 --- a/services/geminiService.ts +++ b/services/geminiService.ts @@ -1,61 +1,51 @@ import { GoogleGenAI } from "@google/genai"; import { KBArticle, Ticket, TicketStatus } from "../types"; -// The API key must be obtained exclusively from the environment variable process.env.API_KEY. -const apiKey = process.env.API_KEY || ''; - -// Initialize AI only if key exists to prevent immediate instantiation errors -let ai: GoogleGenAI | null = null; -if (apiKey) { - try { - ai = new GoogleGenAI({ apiKey }); - } catch (e) { - console.warn("Failed to initialize GoogleGenAI", e); - } -} - /** * Agent 1: Customer Support Chat * Uses the KB to answer questions. */ export const getSupportResponse = async ( + apiKey: string, userQuery: string, chatHistory: string[], knowledgeBase: KBArticle[] ): Promise => { - if (!ai || !apiKey) { - return "L'assistente AI non è configurato o la chiave API è mancante. Contatta l'amministratore."; + if (!apiKey) { + return "L'assistente AI non è configurato (API Key mancante). Contatta l'amministratore."; } - // Prepare Context from KB - const kbContext = knowledgeBase.map(a => { - if (a.type === 'url') { - return `Fonte Esterna [${a.category}]: ${a.title} - URL: ${a.url}\nContenuto Estratto: ${a.content}`; - } - return `Articolo [${a.category}]: ${a.title}\nContenuto: ${a.content}`; - }).join('\n\n'); - - const systemInstruction = ` - Sei "OmniSupport AI", un assistente clienti virtuale globale. - - IL TUO COMPITO: - Rispondere alle domande dei clienti basandoti ESCLUSIVAMENTE sulla seguente Base di Conoscenza (KB) fornita in ITALIANO. - - GESTIONE LINGUA (IMPORTANTE): - 1. Rileva automaticamente la lingua utilizzata dall'utente nel suo ultimo messaggio. - 2. Anche se la KB è in Italiano, devi tradurre mentalmente la richiesta, cercare la risposta nella KB Italiana, e poi RISPONDERE NELLA LINGUA DELL'UTENTE. - - BASE DI CONOSCENZA (ITALIANO): - ${kbContext} - - REGOLE: - 1. Se la risposta è nella KB, forniscila. - 2. Se l'articolo è una fonte web (URL), usa il "Contenuto Estratto" per rispondere e fornisci anche il link originale all'utente. - 3. Se la risposta NON si trova nella KB, ammettilo gentilmente (nella lingua dell'utente) e consiglia di aprire un ticket. - 4. Sii cortese, professionale e sintetico. - `; - try { + const ai = new GoogleGenAI({ apiKey }); + + // Prepare Context from KB + const kbContext = knowledgeBase.map(a => { + if (a.type === 'url') { + return `Fonte Esterna [${a.category}]: ${a.title} - URL: ${a.url}\nContenuto Estratto: ${a.content}`; + } + return `Articolo [${a.category}]: ${a.title}\nContenuto: ${a.content}`; + }).join('\n\n'); + + const systemInstruction = ` + Sei "OmniSupport AI", un assistente clienti virtuale globale. + + IL TUO COMPITO: + Rispondere alle domande dei clienti basandoti ESCLUSIVAMENTE sulla seguente Base di Conoscenza (KB) fornita in ITALIANO. + + GESTIONE LINGUA (IMPORTANTE): + 1. Rileva automaticamente la lingua utilizzata dall'utente nel suo ultimo messaggio. + 2. Anche se la KB è in Italiano, devi tradurre mentalmente la richiesta, cercare la risposta nella KB Italiana, e poi RISPONDERE NELLA LINGUA DELL'UTENTE. + + BASE DI CONOSCENZA (ITALIANO): + ${kbContext} + + REGOLE: + 1. Se la risposta è nella KB, forniscila. + 2. Se l'articolo è una fonte web (URL), usa il "Contenuto Estratto" per rispondere e fornisci anche il link originale all'utente. + 3. Se la risposta NON si trova nella KB, ammettilo gentilmente (nella lingua dell'utente) e consiglia di aprire un ticket. + 4. Sii cortese, professionale e sintetico. + `; + const response = await ai.models.generateContent({ model: 'gemini-3-flash-preview', contents: [ @@ -71,7 +61,7 @@ export const getSupportResponse = async ( return response.text || "Mi dispiace, non riesco a generare una risposta al momento."; } catch (error) { console.error("Gemini Error:", error); - return "Si è verificato un errore nel servizio AI."; + return "Si è verificato un errore nel servizio AI (Verifica API Key o connessione)."; } }; @@ -79,48 +69,51 @@ export const getSupportResponse = async ( * Agent 2: Knowledge Extraction */ export const generateNewKBArticle = async ( + apiKey: string, resolvedTickets: Ticket[], existingArticles: KBArticle[] ): Promise<{ title: string; content: string; category: string } | null> => { - if (!ai || !apiKey) return null; - - // Filter only resolved tickets - const relevantTickets = resolvedTickets.filter(t => t.status === TicketStatus.RESOLVED); - - if (relevantTickets.length === 0) return null; - - // Aggregate ticket conversations - const transcripts = relevantTickets.map(t => { - const convo = t.messages.map(m => `${m.role.toUpperCase()}: ${m.content}`).join('\n'); - return `TICKET ID: ${t.id}\nOGGETTO: ${t.subject}\nCONVERSAZIONE:\n${convo}\n---`; - }).join('\n'); - - const existingTitles = existingArticles.map(a => a.title).join(', '); - - const prompt = ` - Sei un Knowledge Manager AI esperto. - Analizza i seguenti ticket risolti e confrontali con gli articoli esistenti nella Knowledge Base. - - ARTICOLI ESISTENTI: ${existingTitles} - - TICKET RISOLTI RECENTI: - ${transcripts} - - OBIETTIVO: - 1. Identifica un problema ricorrente o una soluzione tecnica presente nei ticket risolti MA NON coperta dagli articoli esistenti. - 2. Se trovi una lacuna, scrivi un NUOVO articolo di Knowledge Base per colmarla. - 3. Restituisci il risultato ESCLUSIVAMENTE in formato JSON. - - SCHEMA JSON RICHIESTO: - { - "foundGap": boolean, - "title": "Titolo del nuovo articolo", - "content": "Contenuto dettagliato in formato Markdown", - "category": "Categoria suggerita" - } - `; + if (!apiKey) return null; try { + const ai = new GoogleGenAI({ apiKey }); + + // Filter only resolved tickets + const relevantTickets = resolvedTickets.filter(t => t.status === TicketStatus.RESOLVED); + + if (relevantTickets.length === 0) return null; + + // Aggregate ticket conversations + const transcripts = relevantTickets.map(t => { + const convo = t.messages.map(m => `${m.role.toUpperCase()}: ${m.content}`).join('\n'); + return `TICKET ID: ${t.id}\nOGGETTO: ${t.subject}\nCONVERSAZIONE:\n${convo}\n---`; + }).join('\n'); + + const existingTitles = existingArticles.map(a => a.title).join(', '); + + const prompt = ` + Sei un Knowledge Manager AI esperto. + Analizza i seguenti ticket risolti e confrontali con gli articoli esistenti nella Knowledge Base. + + ARTICOLI ESISTENTI: ${existingTitles} + + TICKET RISOLTI RECENTI: + ${transcripts} + + OBIETTIVO: + 1. Identifica un problema ricorrente o una soluzione tecnica presente nei ticket risolti MA NON coperta dagli articoli esistenti. + 2. Se trovi una lacuna, scrivi un NUOVO articolo di Knowledge Base per colmarla. + 3. Restituisci il risultato ESCLUSIVAMENTE in formato JSON. + + SCHEMA JSON RICHIESTO: + { + "foundGap": boolean, + "title": "Titolo del nuovo articolo", + "content": "Contenuto dettagliato in formato Markdown", + "category": "Categoria suggerita" + } + `; + const response = await ai.models.generateContent({ model: 'gemini-3-pro-preview', contents: prompt,