Spaces:
Sleeping
Sleeping
| import logger from '../utils/logger.js'; | |
| import { withRetry, withTimeout } from '../utils/retry.js'; | |
| class AIProvider { | |
| constructor(config) { | |
| this.provider = config.ai.provider || 'fallback'; | |
| this.config = config.ai; | |
| this._requestCount = 0; | |
| this._errorCount = 0; | |
| this._lastError = null; | |
| this._lastRequestTime = 0; | |
| this._minRequestInterval = 500; | |
| this._usedIndices = new Map(); | |
| this._sessionStart = Date.now(); | |
| } | |
| async generate(prompt, options = {}) { | |
| const { maxTokens = 1024, temperature = 0.7, systemPrompt = null } = options; | |
| this._requestCount++; | |
| const now = Date.now(); | |
| const timeSinceLastRequest = now - this._lastRequestTime; | |
| if (timeSinceLastRequest < this._minRequestInterval) { | |
| await new Promise(resolve => setTimeout(resolve, this._minRequestInterval - timeSinceLastRequest)); | |
| } | |
| try { | |
| if (this.provider === 'ollama') return await this._ollama(prompt, { maxTokens, temperature, systemPrompt }); | |
| if (this.provider === 'huggingface') return await this._huggingface(prompt, { maxTokens, temperature, systemPrompt }); | |
| if (this.provider === 'groq') return await this._groq(prompt, { maxTokens, temperature, systemPrompt }); | |
| if (this.provider === 'openrouter') return await this._openrouter(prompt, { maxTokens, temperature, systemPrompt }); | |
| if (this.provider === 'fallback') return this._fallbackResponse(prompt); | |
| throw new Error(`Unknown AI provider: ${this.provider}`); | |
| } catch (error) { | |
| this._errorCount++; | |
| this._lastError = error; | |
| logger.error(`AI generation failed (attempt ${this._requestCount}): ${error.message}`); | |
| return this._fallbackResponse(prompt); | |
| } finally { | |
| this._lastRequestTime = Date.now(); | |
| } | |
| } | |
| async _ollama(prompt, options) { | |
| const { maxTokens, temperature, systemPrompt } = options; | |
| const timeout = this.config.ollama.timeout; | |
| const maxRetries = this.config.ollama.maxRetries; | |
| return withRetry(async () => { | |
| const body = { | |
| model: this.config.ollama.model, | |
| prompt: systemPrompt ? `${systemPrompt}\n\n${prompt}` : prompt, | |
| stream: false, | |
| options: { temperature, num_predict: maxTokens }, | |
| }; | |
| const response = await withTimeout( | |
| fetch(`${this.config.ollama.baseUrl}/api/generate`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(body), | |
| }), | |
| timeout, | |
| new Error(`Ollama request timed out after ${timeout}ms`) | |
| ); | |
| if (!response.ok) { | |
| const errorText = await response.text().catch(() => 'Unknown error'); | |
| throw new Error(`Ollama API error ${response.status}: ${errorText}`); | |
| } | |
| const data = await response.json(); | |
| if (!data.response || data.response.trim() === '') throw new Error('Ollama returned empty response'); | |
| return data.response; | |
| }, { maxRetries, baseDelay: 2000, maxDelay: 30000 }); | |
| } | |
| async _huggingface(prompt, options) { | |
| const { maxTokens, temperature, systemPrompt } = options; | |
| const timeout = this.config.huggingface.timeout; | |
| const maxRetries = this.config.huggingface.maxRetries; | |
| if (!this.config.huggingface.apiKey) throw new Error('HuggingFace API key is required'); | |
| return withRetry(async () => { | |
| const fullPrompt = systemPrompt ? `${systemPrompt}\n\n${prompt}` : prompt; | |
| const response = await withTimeout( | |
| fetch(`https://router.huggingface.co/hf-inference/models/${this.config.huggingface.model}`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.config.huggingface.apiKey}` }, | |
| body: JSON.stringify({ inputs: fullPrompt, parameters: { max_new_tokens: maxTokens, temperature, return_full_text: false } }), | |
| }), | |
| timeout, | |
| new Error(`HuggingFace request timed out after ${timeout}ms`) | |
| ); | |
| if (!response.ok) { | |
| const errorText = await response.text().catch(() => 'Unknown error'); | |
| throw new Error(`HuggingFace API error ${response.status}: ${errorText}`); | |
| } | |
| const data = await response.json(); | |
| if (Array.isArray(data) && data[0]?.generated_text) return data[0].generated_text.trim(); | |
| if (data.generated_text) return data.generated_text.trim(); | |
| if (data.error) throw new Error(`HuggingFace error: ${data.error}`); | |
| return ''; | |
| }, { maxRetries, baseDelay: 3000, maxDelay: 60000 }); | |
| } | |
| async _groq(prompt, options) { | |
| const { maxTokens, temperature, systemPrompt } = options; | |
| const timeout = this.config.groq?.timeout || 30000; | |
| const maxRetries = this.config.groq?.maxRetries || 3; | |
| const model = this.config.groq?.model || 'llama-3.1-8b-instant'; | |
| return withRetry(async () => { | |
| const messages = []; | |
| if (systemPrompt) messages.push({ role: 'system', content: systemPrompt }); | |
| messages.push({ role: 'user', content: prompt }); | |
| const response = await withTimeout( | |
| fetch('https://api.groq.com/openai/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.config.groq?.apiKey}` }, | |
| body: JSON.stringify({ model, messages, max_tokens: maxTokens, temperature }), | |
| }), | |
| timeout, | |
| new Error(`Groq request timed out after ${timeout}ms`) | |
| ); | |
| if (!response.ok) { | |
| const errorText = await response.text().catch(() => 'Unknown error'); | |
| throw new Error(`Groq API error ${response.status}: ${errorText}`); | |
| } | |
| const data = await response.json(); | |
| if (data.choices && data.choices[0]?.message?.content) return data.choices[0].message.content.trim(); | |
| if (data.error) throw new Error(`Groq error: ${data.error.message || data.error}`); | |
| return ''; | |
| }, { maxRetries, baseDelay: 1000, maxDelay: 30000 }); | |
| } | |
| async _openrouter(prompt, options) { | |
| const { maxTokens, temperature, systemPrompt } = options; | |
| const timeout = this.config.openrouter?.timeout || 30000; | |
| const maxRetries = this.config.openrouter?.maxRetries || 3; | |
| const model = this.config.openrouter?.model || 'meta-llama/llama-3.1-8b-instruct:free'; | |
| return withRetry(async () => { | |
| const messages = []; | |
| if (systemPrompt) messages.push({ role: 'system', content: systemPrompt }); | |
| messages.push({ role: 'user', content: prompt }); | |
| const response = await withTimeout( | |
| fetch('https://openrouter.ai/api/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| Authorization: `Bearer ${this.config.openrouter?.apiKey}`, | |
| 'HTTP-Referer': 'https://github.com/abedmohamed258/activity-simulator', | |
| 'X-Title': 'Activity Simulator', | |
| }, | |
| body: JSON.stringify({ model, messages, max_tokens: maxTokens, temperature }), | |
| }), | |
| timeout, | |
| new Error(`OpenRouter request timed out after ${timeout}ms`) | |
| ); | |
| if (!response.ok) { | |
| const errorText = await response.text().catch(() => 'Unknown error'); | |
| throw new Error(`OpenRouter API error ${response.status}: ${errorText}`); | |
| } | |
| const data = await response.json(); | |
| if (data.choices && data.choices[0]?.message?.content) return data.choices[0].message.content.trim(); | |
| if (data.error) throw new Error(`OpenRouter error: ${data.error.message || data.error}`); | |
| return ''; | |
| }, { maxRetries, baseDelay: 1000, maxDelay: 30000 }); | |
| } | |
| _getUnusedItem(key, items) { | |
| if (!this._usedIndices.has(key)) this._usedIndices.set(key, new Set()); | |
| const used = this._usedIndices.get(key); | |
| if (used.size >= items.length) { | |
| used.clear(); | |
| } | |
| let idx; | |
| do { | |
| idx = Math.floor(Math.random() * items.length); | |
| } while (used.has(idx) && used.size < items.length); | |
| used.add(idx); | |
| return items[idx]; | |
| } | |
| _fallbackResponse(prompt) { | |
| const lowerPrompt = prompt.toLowerCase(); | |
| if (lowerPrompt.includes('github issue title') || lowerPrompt.includes('issue title')) { | |
| return this._getUnusedItem('issueTitles', this._issueTitles); | |
| } | |
| if (lowerPrompt.includes('issue body') || lowerPrompt.includes('github issue')) { | |
| return this._getUnusedItem('issueBodies', this._issueBodies); | |
| } | |
| if (lowerPrompt.includes('review decision') || lowerPrompt.includes('approve')) { | |
| return this._getUnusedItem('reviewDecisions', this._reviewDecisions); | |
| } | |
| if (lowerPrompt.includes('issue') || lowerPrompt.includes('problem')) { | |
| return this._getUnusedItem('issueComments', this._issueComments); | |
| } | |
| if (lowerPrompt.includes('review') || lowerPrompt.includes('comment')) { | |
| return this._getUnusedItem('reviewComments', this._reviewComments); | |
| } | |
| if (lowerPrompt.includes('code') || lowerPrompt.includes('function')) { | |
| return this._getUnusedItem('codeSnippets', this._codeSnippets); | |
| } | |
| if (lowerPrompt.includes('pull request') || lowerPrompt.includes('pr description')) { | |
| return this._getUnusedItem('prDescriptions', this._prDescriptions); | |
| } | |
| if (lowerPrompt.includes('clarif') || lowerPrompt.includes('question')) { | |
| return this._getUnusedItem('clarifications', this._clarifications); | |
| } | |
| return this._getUnusedItem('defaultResponses', this._defaultResponses); | |
| } | |
| get _issueTitles() { | |
| return [ | |
| 'Add input validation to user registration form', | |
| 'Fix race condition in concurrent file processing', | |
| 'Refactor database connection pooling for better performance', | |
| 'Add proper error handling to API endpoints', | |
| 'Implement caching layer for frequently accessed data', | |
| 'Fix memory leak in event listener cleanup', | |
| 'Add unit tests for utility functions', | |
| 'Improve accessibility for screen readers', | |
| 'Optimize database queries for large datasets', | |
| 'Add rate limiting to prevent abuse', | |
| 'Update dependencies to latest versions', | |
| 'Add logging for debugging production issues', | |
| 'Fix CSS layout shift on page load', | |
| 'Implement proper TypeScript types for API responses', | |
| 'Add CI pipeline for automated testing', | |
| 'Fix broken link in documentation', | |
| 'Add dark mode support to the UI', | |
| 'Improve startup time by lazy loading modules', | |
| 'Add pagination to the dashboard table', | |
| 'Fix timezone handling in date picker', | |
| 'Add WebSocket support for real-time updates', | |
| 'Fix null pointer exception in data processor', | |
| 'Implement graceful shutdown for background workers', | |
| 'Add request timeout configuration to HTTP client', | |
| 'Fix duplicate event listener registration', | |
| 'Add health check endpoint for monitoring', | |
| 'Implement exponential backoff for retry logic', | |
| 'Fix circular dependency in module imports', | |
| 'Add request ID tracing for distributed logging', | |
| 'Implement connection pooling for database client', | |
| 'Fix incorrect date formatting in reports', | |
| 'Add support for custom error pages', | |
| 'Implement file upload size validation', | |
| 'Fix memory usage spike during batch processing', | |
| 'Add CORS configuration for cross-origin requests', | |
| 'Implement proper session management', | |
| 'Fix inconsistent error response format', | |
| 'Add support for environment-specific configurations', | |
| 'Implement database migration rollback capability', | |
| 'Fix incorrect pagination offset calculation', | |
| 'Add support for bulk operations in API', | |
| 'Implement request deduplication for concurrent calls', | |
| 'Fix thread safety issue in shared cache', | |
| 'Add support for custom middleware pipeline', | |
| 'Implement graceful degradation for external service failures', | |
| 'Fix incorrect content-type header in responses', | |
| 'Add support for API versioning', | |
| 'Implement proper input sanitization for XSS prevention', | |
| 'Fix incorrect sorting order in list endpoints', | |
| 'Add support for background job scheduling', | |
| ]; | |
| } | |
| get _issueBodies() { | |
| return [ | |
| '## Description\n\nThe current implementation does not handle null inputs gracefully, which can cause unexpected crashes in production.\n\n## Steps to Reproduce\n\n1. Send a request with null payload\n2. Observe the server crash\n3. Check error logs\n\n## Expected Behavior\n\nThe server should return a 400 Bad Request error with a helpful message.\n\n## Actual Behavior\n\nThe server crashes with an unhandled exception.', | |
| '## Description\n\nPerformance degrades significantly when processing large datasets (>10k items).\n\n## Steps to Reproduce\n\n1. Load a dataset with 50k items\n2. Run the processing pipeline\n3. Observe memory usage and response time\n\n## Expected Behavior\n\nProcessing should complete within 5 seconds with stable memory usage.\n\n## Actual Behavior\n\nProcessing takes over 30 seconds and memory usage spikes to 2GB.', | |
| '## Description\n\nThe documentation is outdated and does not reflect the current API surface.\n\n## Steps to Reproduce\n\n1. Follow the quickstart guide\n2. Try to run the example code\n\n## Expected Behavior\n\nThe example should work as documented.\n\n## Actual Behavior\n\nSeveral methods have been renamed or removed without documentation updates.', | |
| '## Description\n\nThe authentication flow has a race condition that can cause token refresh failures.\n\n## Steps to Reproduce\n\n1. Log in with valid credentials\n2. Wait for token to expire\n3. Make multiple concurrent API calls\n\n## Expected Behavior\n\nAll requests should use the refreshed token.\n\n## Actual Behavior\n\nSome requests fail with 401 Unauthorized due to stale tokens.', | |
| '## Description\n\nThe error handling middleware does not catch all exception types.\n\n## Steps to Reproduce\n\n1. Trigger a database connection error\n2. Observe the error response\n\n## Expected Behavior\n\nA structured 500 error response should be returned.\n\n## Actual Behavior\n\nRaw error details are exposed in the response.', | |
| '## Description\n\nThe caching layer does not invalidate properly when data changes.\n\n## Steps to Reproduce\n\n1. Fetch a resource (gets cached)\n2. Update the resource\n3. Fetch again\n\n## Expected Behavior\n\nThe updated resource should be returned.\n\n## Actual Behavior\n\nThe stale cached version is returned until TTL expires.', | |
| '## Description\n\nThe logging system does not include request context in log entries.\n\n## Steps to Reproduce\n\n1. Enable debug logging\n2. Make several concurrent requests\n3. Check the logs\n\n## Expected Behavior\n\nEach log entry should include request ID and user context.\n\n## Actual Behavior\n\nLog entries are mixed and hard to correlate with specific requests.', | |
| '## Description\n\nThe file upload feature does not validate file types properly.\n\n## Steps to Reproduce\n\n1. Upload a file with an incorrect extension\n2. Observe the server response\n\n## Expected Behavior\n\nThe server should reject unsupported file types.\n\n## Actual Behavior\n\nThe file is accepted but causes processing errors later.', | |
| ]; | |
| } | |
| get _reviewComments() { | |
| return [ | |
| 'Looks good! Just one suggestion: add error handling for the edge case where the input is null.', | |
| 'Nice work on the refactoring. The code is much cleaner now.', | |
| 'I noticed a potential race condition here. Should we add a lock?', | |
| 'Great implementation! Consider adding unit tests for the new functions.', | |
| 'This approach works, but we might want to use a more efficient algorithm for large inputs.', | |
| 'LGTM! Ready to merge.', | |
| 'Small nit: the variable name could be more descriptive.', | |
| 'Good catch on the bug fix. The test coverage looks solid.', | |
| 'I\'d suggest extracting this logic into a separate function for reusability.', | |
| 'The error messages here are really helpful for debugging. Nice touch!', | |
| 'Should we add a timeout to this external API call?', | |
| 'The documentation update is thorough. Thanks for keeping it current.', | |
| 'I wonder if we could simplify this with a reduce instead of the for loop.', | |
| 'The performance improvement is significant. Great optimization!', | |
| 'Could we add a comment explaining why this workaround is needed?', | |
| 'The test cases cover all the edge cases I could think of. Well done.', | |
| 'I\'m not sure about hardcoding this value. Maybe a config option?', | |
| 'The code structure is much better now. Much easier to follow.', | |
| 'Should we consider adding a retry mechanism here?', | |
| 'The type definitions are comprehensive. This will save us from bugs later.', | |
| 'I like the approach. One thing: we should probably validate the input earlier.', | |
| 'The commit history is clean and well-organized. Great job.', | |
| 'This change makes the API much more intuitive to use.', | |
| 'Should we add a deprecation warning for the old method?', | |
| 'The benchmark results are impressive. Nice performance win.', | |
| ]; | |
| } | |
| get _issueComments() { | |
| return [ | |
| 'I can reproduce this. Looking into it now.', | |
| 'Good find! This has been causing issues in production too.', | |
| 'I think the root cause is in the validation layer. Let me check.', | |
| 'Thanks for the detailed report. This helps a lot.', | |
| 'I\'ve seen this before. It\'s related to the caching issue we fixed last week.', | |
| 'Can you share the exact version you\'re running? This might be version-specific.', | |
| 'I\'ll take a look at this tomorrow. Seems like a priority fix.', | |
| 'This is on my radar. Working on a fix this sprint.', | |
| 'Interesting. Does this happen consistently or intermittently?', | |
| 'I suspect this is related to the recent dependency update.', | |
| 'Let me know if you need help reproducing this. I have a test environment ready.', | |
| 'This affects our SLA. Marking as high priority.', | |
| 'I\'ve added a workaround for now. Will implement a proper fix next week.', | |
| 'The fix is in progress. Should have a PR ready by end of day.', | |
| 'This is a known limitation. We\'re planning to address it in the next major release.', | |
| ]; | |
| } | |
| get _reviewDecisions() { | |
| return ['approve', 'approve', 'approve', 'approve', 'comment', 'comment', 'request_changes']; | |
| } | |
| get _codeSnippets() { | |
| return [ | |
| '```javascript\nfunction processData(items) {\n return items\n .filter(item => item != null)\n .map(item => ({ ...item, processed: true }));\n}\n```', | |
| '```javascript\nclass Cache {\n constructor(ttl = 300) {\n this.store = new Map();\n this.ttl = ttl;\n }\n\n get(key) {\n const entry = this.store.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > this.ttl * 1000) {\n this.store.delete(key);\n return null;\n }\n return entry.value;\n }\n\n set(key, value) {\n this.store.set(key, { value, timestamp: Date.now() });\n }\n}\n```', | |
| '```javascript\nasync function fetchWithRetry(url, options = {}, maxRetries = 3) {\n for (let i = 0; i < maxRetries; i++) {\n try {\n const response = await fetch(url, options);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n return response;\n } catch (error) {\n if (i === maxRetries - 1) throw error;\n await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));\n }\n }\n}\n```', | |
| '```javascript\nfunction validateInput(schema, data) {\n const errors = [];\n for (const [key, rules] of Object.entries(schema)) {\n if (rules.required && (data[key] === undefined || data[key] === null)) {\n errors.push(`${key} is required`);\n }\n if (rules.type && data[key] !== undefined && typeof data[key] !== rules.type) {\n errors.push(`${key} must be of type ${rules.type}`);\n }\n }\n return { valid: errors.length === 0, errors };\n}\n```', | |
| '```javascript\nclass EventEmitter {\n constructor() {\n this.listeners = new Map();\n }\n\n on(event, callback) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, []);\n }\n this.listeners.get(event).push(callback);\n return () => this.off(event, callback);\n }\n\n off(event, callback) {\n const callbacks = this.listeners.get(event) || [];\n this.listeners.set(event, callbacks.filter(cb => cb !== callback));\n }\n\n emit(event, ...args) {\n const callbacks = this.listeners.get(event) || [];\n callbacks.forEach(cb => cb(...args));\n }\n}\n```', | |
| ]; | |
| } | |
| get _prDescriptions() { | |
| return [ | |
| '## Summary\n\nThis PR implements the requested feature with proper error handling and tests.\n\n## Changes\n\n- Added new functionality\n- Updated existing code\n- Added tests\n\n## Testing\n\n- [x] Manual testing completed\n- [x] Unit tests pass', | |
| '## Summary\n\nRefactors the data processing pipeline for better performance and maintainability.\n\n## Changes\n\n- Extracted common logic into utility functions\n- Added proper error boundaries\n- Improved test coverage to 85%\n\n## Performance\n\nProcessing time reduced by 40% for large datasets.', | |
| '## Summary\n\nFixes the authentication race condition by implementing proper token synchronization.\n\n## Changes\n\n- Added token refresh lock\n- Implemented request queue during refresh\n- Added integration tests\n\n## Testing\n\nVerified with concurrent request simulation (50 simultaneous requests).', | |
| ]; | |
| } | |
| get _clarifications() { | |
| return [ | |
| 'Can you share the exact error message you\'re seeing?', | |
| 'What version of the library are you using?', | |
| 'Does this happen on all environments or just production?', | |
| 'Can you provide a minimal reproduction case?', | |
| 'What browser/OS are you seeing this on?', | |
| 'Is this a regression from a previous version?', | |
| ]; | |
| } | |
| get _defaultResponses() { | |
| return [ | |
| 'This looks reasonable. Let me review the implementation details.', | |
| 'Good approach. I\'ll take a closer look at the edge cases.', | |
| 'Thanks for the update. I\'ll review this shortly.', | |
| 'Makes sense. Let me check if there are any dependencies affected.', | |
| 'I\'ll add some thoughts after I test this locally.', | |
| ]; | |
| } | |
| async generateCode(context, task) { | |
| const prompt = `You are a senior software developer writing production-quality JavaScript code. | |
| Context: ${context} | |
| Task: ${task} | |
| Requirements: | |
| - Write clean, well-structured code | |
| - Include JSDoc comments for public functions | |
| - Handle edge cases and errors gracefully | |
| - Use modern JavaScript features (ES2022+) | |
| - Export functions properly | |
| Return ONLY the code, no explanations.`; | |
| return this.generate(prompt, { maxTokens: 2048, temperature: 0.6 }); | |
| } | |
| async generateIssueTitle(projectContext) { | |
| const prompt = `Generate a realistic GitHub issue title for a software project. | |
| Current project context: ${projectContext} | |
| Make it specific, actionable, and realistic. Examples: | |
| - "Add input validation to user registration form" | |
| - "Fix race condition in concurrent file processing" | |
| - "Refactor database connection pooling for better performance" | |
| Return ONLY the title, nothing else.`; | |
| return (await this.generate(prompt, { maxTokens: 100, temperature: 0.8 })).trim(); | |
| } | |
| async generateIssueBody(title, projectContext) { | |
| const prompt = `Write a realistic GitHub issue body for the following issue: | |
| Title: ${title} | |
| Project context: ${projectContext} | |
| Include: | |
| - Problem description | |
| - Steps to reproduce (if applicable) | |
| - Expected vs actual behavior | |
| - Environment details | |
| Keep it concise and professional.`; | |
| return this.generate(prompt, { maxTokens: 1024, temperature: 0.7 }); | |
| } | |
| async generatePRDescription(branchName, changes) { | |
| const prompt = `Write a professional GitHub Pull Request description. | |
| Branch: ${branchName} | |
| Changes summary: ${changes} | |
| Include: | |
| - What this PR does | |
| - Why these changes are needed | |
| - Testing done | |
| - Any breaking changes | |
| Use markdown formatting.`; | |
| return this.generate(prompt, { maxTokens: 1024, temperature: 0.7 }); | |
| } | |
| async generateReviewComment(code, fileName) { | |
| const prompt = `You are reviewing code in a GitHub Pull Request. | |
| File: ${fileName} | |
| Code: | |
| \`\`\`javascript | |
| ${code} | |
| \`\`\` | |
| Write a realistic, professional code review comment. Be specific about the code. | |
| Possible angles: | |
| - Suggest improvements | |
| - Point out potential bugs | |
| - Ask clarifying questions | |
| - Praise good patterns | |
| Keep it to 1-3 sentences. Be natural, not robotic.`; | |
| return this.generate(prompt, { maxTokens: 256, temperature: 0.8 }); | |
| } | |
| async generateCommitMessage(changes) { | |
| const prompt = `Write a concise, professional git commit message for these changes: | |
| ${changes} | |
| Follow conventional commits format when appropriate. | |
| Examples: "feat: add user authentication middleware" | |
| "fix: handle null values in data processor" | |
| "refactor: simplify database query logic" | |
| Return ONLY the commit message, nothing else.`; | |
| return (await this.generate(prompt, { maxTokens: 100, temperature: 0.7 })).trim(); | |
| } | |
| async generateReviewDecision(prTitle, prBody) { | |
| const prompt = `You are reviewing a Pull Request. | |
| Title: ${prTitle} | |
| Body: ${prBody?.slice(0, 300) || 'No description provided'} | |
| Decide on the review outcome. Return ONLY one of these words: | |
| - "approve" if the code looks good and ready to merge | |
| - "comment" if you have minor suggestions but overall it's fine | |
| - "request_changes" if there are significant issues | |
| Return ONLY the decision word, nothing else.`; | |
| return (await this.generate(prompt, { maxTokens: 20, temperature: 0.8 })).trim().toLowerCase(); | |
| } | |
| getStats() { | |
| return { | |
| totalRequests: this._requestCount, | |
| totalErrors: this._errorCount, | |
| errorRate: this._requestCount > 0 ? (this._errorCount / this._requestCount * 100).toFixed(1) : 0, | |
| lastError: this._lastError?.message || null, | |
| provider: this.provider, | |
| sessionDuration: Date.now() - this._sessionStart, | |
| }; | |
| } | |
| } | |
| export default AIProvider; | |