Files
adopt-a-street/backend/routes/ai.js
William Valentin 771d39a52b feat: integrate OpenAI API for intelligent task suggestions
Implement real OpenAI integration for the AI task suggestions feature:

- Create aiService.js with GPT-3.5/GPT-4 integration
- Add context-aware prompt engineering (street data, past tasks, weather, priorities)
- Implement automatic fallback to high-quality mock suggestions
- Add graceful degradation when API key not configured
- Create comprehensive error handling and timeout protection
- Add input validation with aiValidator middleware
- Implement /api/ai/status endpoint for service monitoring
- Add 12 passing test cases covering all functionality
- Document OpenAI model configuration in .env.example
- Create detailed AI_SERVICE.md documentation

Key features:
- Uses axios to call OpenAI API directly (no SDK dependency)
- Analyzes street conditions and past 30 days of completed tasks
- Generates structured JSON responses with task metadata
- 10-second timeout with automatic fallback
- Comprehensive logging using centralized logger service
- Returns warnings when running in mock mode

All tests pass (12/12). Ready for production use with or without API key.

🤖 Generated with AI Assistant

Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
2025-11-03 13:19:00 -08:00

131 lines
3.5 KiB
JavaScript

const express = require("express");
const auth = require("../middleware/auth");
const aiService = require("../services/aiService");
const logger = require("../utils/logger");
const { taskSuggestionsValidation } = require("../middleware/validators/aiValidator");
const Street = require("../models/Street");
const Task = require("../models/Task");
const router = express.Router();
// Get AI service status
router.get("/status", auth, async (req, res) => {
try {
const status = aiService.getStatus();
res.json({
success: true,
data: status
});
} catch (err) {
logger.error("Error getting AI service status", err);
res.status(500).json({
success: false,
msg: "Server error"
});
}
});
// Get AI task suggestions
router.get("/task-suggestions", auth, taskSuggestionsValidation, async (req, res) => {
try {
const { streetId, count = 5 } = req.query;
const numberOfSuggestions = parseInt(count, 10);
let streetName = "General Street";
let location = null;
let pastTasks = [];
// If streetId is provided, fetch street details and past tasks
if (streetId) {
const street = await Street.findById(streetId);
if (!street) {
return res.status(404).json({
success: false,
msg: "Street not found"
});
}
streetName = street.name;
location = street.location;
// Fetch past completed tasks for this street
const tasks = await Task.find({
"street.streetId": streetId,
status: "completed"
});
// Extract task descriptions from past 30 days
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
pastTasks = tasks
.filter(task => new Date(task.completedAt) > thirtyDaysAgo)
.map(task => task.description)
.slice(0, 10); // Limit to 10 most recent tasks
logger.info("Fetching AI suggestions for specific street", {
streetId,
streetName,
pastTasksCount: pastTasks.length
});
} else {
logger.info("Fetching general AI suggestions");
}
// Mock weather data (in production, this could call a weather API)
const weather = {
condition: "Partly cloudy",
description: "Clear with mild temperatures",
temperature: 72
};
// Example community priorities (in production, these could come from database)
const communityPriorities = [
"Safety improvements",
"Environmental sustainability",
"Neighborhood beautification"
];
// Generate suggestions using AI service
const suggestions = await aiService.generateTaskSuggestions({
streetName,
location,
pastTasks,
weather,
communityPriorities,
numberOfSuggestions
});
// Add warning if using mock data
const response = {
success: true,
data: suggestions,
meta: {
source: suggestions[0]?.source || "unknown",
count: suggestions.length,
aiConfigured: aiService.isAvailable()
}
};
if (!aiService.isAvailable()) {
response.warning = "OpenAI API not configured. Returning mock suggestions. Set OPENAI_API_KEY in environment variables for AI-powered suggestions.";
}
res.json(response);
} catch (err) {
logger.error("Error generating task suggestions", err, {
streetId: req.query.streetId,
userId: req.user.id
});
res.status(500).json({
success: false,
msg: "Server error while generating task suggestions"
});
}
});
module.exports = router;