UNPKG

@notes-sync/service

Version:

Background service for AI-powered note synchronization

148 lines (144 loc) 6.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GeminiProvider = void 0; const shared_1 = require("@notes-sync/shared"); const genai_1 = require("@google/genai"); const logger_1 = require("../../logger"); class GeminiProvider { constructor(apiKey, model = 'gemini-2.5-flash-lite', quoteConfig) { this.apiKey = apiKey; this.model = model; this.name = 'gemini'; if (!apiKey) { throw new Error('Gemini API key is required'); } // Initialize the Google GenAI client this.ai = new genai_1.GoogleGenAI({ apiKey: this.apiKey }); // Set defaults for quote configuration this.config = { maxLength: quoteConfig?.maxLength || 30, focus: quoteConfig?.focus || ['productivity', 'personal growth'], adjectives: quoteConfig?.adjectives || ['historical', 'motivational'], additionalRules: quoteConfig?.additionalRules || [ 'If quoting from spiritual texts, include the book name as author', 'Prefer wisdom that applies to daily work and life', ], }; } async generateQuote(request) { try { const prompt = this.buildQuotePrompt(request); const response = await this.ai.models.generateContent({ model: this.model, contents: prompt, config: { temperature: 0.8, topK: 20, topP: 0.8, maxOutputTokens: 150, }, }); if (!response.text) { throw new shared_1.AIError('No text content in Gemini API response', this.name); } const parsed = this.parseQuoteResponse(response.text); logger_1.Logger.log(`Generated quote via Gemini: "${parsed.quote}" - ${parsed.author}`); return parsed; } catch (error) { // Handle rate limiting errors if (error instanceof Error && error.message.includes('429')) { throw new shared_1.AIRateLimitError(this.name); } if (error instanceof shared_1.AIError) { throw error; } logger_1.Logger.error(`Gemini quote generation failed: ${error.message}`); throw new shared_1.AIError(`Failed to generate quote: ${error.message}`, this.name); } } buildQuotePrompt(request) { if (!this.config) { return 'Find a short, daily quote for someone focused on productivity in the format -> "Quote text" - Author Name -> Under 50 characters.'; } const theme = request.theme || 'productivity'; const context = request.context ? `\n\nContext about the user's recent work: ${request.context}` : ''; const additionalRulesText = this.config.additionalRules?.length ? `\n- ${this.config.additionalRules.join('\n- ')}` : ''; return `Generate a short, daily quote for someone focused on ${this.config.focus?.join(' and ')}. Theme: ${theme} ${context} Requirements: - Keep it under ${this.config.maxLength} words - Make it ${this.config.adjectives?.join(' and ')} - Include the author's name or passage if from spiritual book of unknown author - Format as: "Quote text" - Author Name - Avoid cliches and focus on practical wisdom${additionalRulesText} Examples: "The man who lies to himself and listens to his own lie comes to a point that he cannot distinguish the truth within him, or around him, and so loses all respect for himself and for others." - Fyodor Dostoyevsky "Pain is inevitable. Suffering is optional. The difference lies in what we choose to do with our pain." - Haruki Murakami "Your future self is counting on what you do today" - Unknown Find a real quote ${this.config.allowGenerated && ' or generate one.'}`; } async processQuery(request) { try { const response = await this.ai.models.generateContent({ model: this.model, contents: request.query, config: { temperature: 0.7, topK: 40, topP: 0.9, maxOutputTokens: request.maxLength || 500, }, }); if (!response.text) { throw new shared_1.AIError('No text content in Gemini API response', this.name); } logger_1.Logger.log(`Processed AI query: ${request.query.substring(0, 50)}...`); return { response: response.text.trim(), }; } catch (error) { // Handle rate limiting errors if (error instanceof Error && error.message.includes('429')) { throw new shared_1.AIRateLimitError(this.name); } if (error instanceof shared_1.AIError) { throw error; } logger_1.Logger.error(`Gemini query processing failed: ${error.message}`); throw new shared_1.AIError(`Failed to process query: ${error.message}`, this.name); } } parseQuoteResponse(text) { // Try to parse quote in format: "Quote text" - Author const quoteMatch = text.match(/[""]([^"""]+)[""] - (.+)/); if (quoteMatch) { return { quote: quoteMatch[1].trim(), author: quoteMatch[2].trim(), }; } // Fallback: try to find any quoted text const fallbackMatch = text.match(/[""]([^"""]+)[""]/) || text.match(/"([^"]+)"/); if (fallbackMatch) { return { quote: fallbackMatch[1].trim(), author: 'Unknown', }; } // Last resort: use the first line and attribute to Unknown const firstLine = text.split('\n')[0].trim(); return { quote: firstLine, author: 'Unknown', }; } } exports.GeminiProvider = GeminiProvider; //# sourceMappingURL=gemini-provider.js.map