progress
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// src/pages/api/ai/query.ts
|
||||
// src/pages/api/ai/query.ts - Enhanced for micro-task pipeline
|
||||
import type { APIRoute } from 'astro';
|
||||
import { withAPIAuth } from '../../../utils/auth.js';
|
||||
import { apiError, apiServerError, createAuthErrorResponse } from '../../../utils/api.js';
|
||||
@@ -8,8 +8,14 @@ import { aiPipeline } from '../../../utils/aiPipeline.js';
|
||||
export const prerender = false;
|
||||
|
||||
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
|
||||
const RATE_LIMIT_WINDOW = 60 * 1000;
|
||||
const RATE_LIMIT_MAX = 10;
|
||||
|
||||
// Enhanced rate limiting for micro-task architecture
|
||||
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
|
||||
const RATE_LIMIT_MAX = parseInt(process.env.AI_RATE_LIMIT_MAX_REQUESTS || '8', 10); // Reduced due to micro-tasks
|
||||
|
||||
// Micro-task specific rate limiting
|
||||
const MICRO_TASK_RATE_LIMIT = parseInt(process.env.AI_MICRO_TASK_RATE_LIMIT || '30', 10);
|
||||
const microTaskRateLimitStore = new Map<string, { count: number; resetTime: number }>();
|
||||
|
||||
function sanitizeInput(input: string): string {
|
||||
let sanitized = input
|
||||
@@ -41,13 +47,40 @@ function checkRateLimit(userId: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enhanced: Check micro-task rate limiting
|
||||
function checkMicroTaskRateLimit(userId: string): { allowed: boolean; remaining: number } {
|
||||
const now = Date.now();
|
||||
const userLimit = microTaskRateLimitStore.get(userId);
|
||||
|
||||
if (!userLimit || now > userLimit.resetTime) {
|
||||
microTaskRateLimitStore.set(userId, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
|
||||
return { allowed: true, remaining: MICRO_TASK_RATE_LIMIT - 1 };
|
||||
}
|
||||
|
||||
if (userLimit.count >= MICRO_TASK_RATE_LIMIT) {
|
||||
return { allowed: false, remaining: 0 };
|
||||
}
|
||||
|
||||
userLimit.count++;
|
||||
return { allowed: true, remaining: MICRO_TASK_RATE_LIMIT - userLimit.count };
|
||||
}
|
||||
|
||||
function cleanupExpiredRateLimits() {
|
||||
const now = Date.now();
|
||||
|
||||
// Clean up main rate limits
|
||||
for (const [userId, limit] of rateLimitStore.entries()) {
|
||||
if (now > limit.resetTime) {
|
||||
rateLimitStore.delete(userId);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up micro-task rate limits
|
||||
for (const [userId, limit] of microTaskRateLimitStore.entries()) {
|
||||
if (now > limit.resetTime) {
|
||||
microTaskRateLimitStore.delete(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000);
|
||||
@@ -61,73 +94,104 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
|
||||
const userId = authResult.userId;
|
||||
|
||||
// Check main rate limit
|
||||
if (!checkRateLimit(userId)) {
|
||||
return apiError.rateLimit('Rate limit exceeded');
|
||||
}
|
||||
|
||||
// Enhanced: Check micro-task rate limit
|
||||
const microTaskLimit = checkMicroTaskRateLimit(userId);
|
||||
if (!microTaskLimit.allowed) {
|
||||
return apiError.rateLimit(
|
||||
`Micro-task rate limit exceeded. The new AI pipeline uses multiple smaller requests. Please wait before trying again.`
|
||||
);
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { query, mode = 'workflow', taskId: clientTaskId } = body;
|
||||
|
||||
console.log(`[AI API] Received request - TaskId: ${clientTaskId}, Mode: ${mode}, Query length: ${query?.length || 0}`);
|
||||
console.log(`[MICRO-TASK API] Received request - TaskId: ${clientTaskId}, Mode: ${mode}, Query length: ${query?.length || 0}`);
|
||||
console.log(`[MICRO-TASK API] Micro-task rate limit remaining: ${microTaskLimit.remaining}`);
|
||||
|
||||
if (!query || typeof query !== 'string') {
|
||||
console.log(`[AI API] Invalid query for task ${clientTaskId}`);
|
||||
console.log(`[MICRO-TASK API] Invalid query for task ${clientTaskId}`);
|
||||
return apiError.badRequest('Query required');
|
||||
}
|
||||
|
||||
if (!['workflow', 'tool'].includes(mode)) {
|
||||
console.log(`[AI API] Invalid mode for task ${clientTaskId}: ${mode}`);
|
||||
console.log(`[MICRO-TASK API] Invalid mode for task ${clientTaskId}: ${mode}`);
|
||||
return apiError.badRequest('Invalid mode. Must be "workflow" or "tool"');
|
||||
}
|
||||
|
||||
const sanitizedQuery = sanitizeInput(query);
|
||||
if (sanitizedQuery.includes('[FILTERED]')) {
|
||||
console.log(`[AI API] Filtered input detected for task ${clientTaskId}`);
|
||||
console.log(`[MICRO-TASK API] Filtered input detected for task ${clientTaskId}`);
|
||||
return apiError.badRequest('Invalid input detected');
|
||||
}
|
||||
|
||||
const taskId = clientTaskId || `ai_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
||||
|
||||
console.log(`[AI API] About to enqueue task ${taskId}`);
|
||||
console.log(`[MICRO-TASK API] About to enqueue micro-task pipeline ${taskId}`);
|
||||
|
||||
// Use the new AI pipeline instead of direct API calls
|
||||
// Use the enhanced micro-task AI pipeline
|
||||
const result = await enqueueApiCall(() =>
|
||||
aiPipeline.processQuery(sanitizedQuery, mode)
|
||||
, taskId);
|
||||
|
||||
if (!result || !result.recommendation) {
|
||||
return apiServerError.unavailable('No response from AI pipeline');
|
||||
return apiServerError.unavailable('No response from micro-task AI pipeline');
|
||||
}
|
||||
|
||||
// Add processing statistics to the response for debugging/monitoring
|
||||
console.log(`[AI Query] Mode: ${mode}, User: ${userId}, Query length: ${sanitizedQuery.length}`);
|
||||
console.log(`[AI Query] Processing stats:`, result.processingStats);
|
||||
console.log(`[AI Query] Tools: ${result.recommendation.recommended_tools?.length || 0}, Concepts: ${result.recommendation.background_knowledge?.length || 0}`);
|
||||
// Enhanced: Log micro-task statistics
|
||||
const stats = result.processingStats;
|
||||
console.log(`[MICRO-TASK API] Pipeline completed for ${taskId}:`);
|
||||
console.log(` - Mode: ${mode}`);
|
||||
console.log(` - User: ${userId}`);
|
||||
console.log(` - Query length: ${sanitizedQuery.length}`);
|
||||
console.log(` - Processing time: ${stats.processingTimeMs}ms`);
|
||||
console.log(` - Micro-tasks completed: ${stats.microTasksCompleted}`);
|
||||
console.log(` - Micro-tasks failed: ${stats.microTasksFailed}`);
|
||||
console.log(` - Embeddings used: ${stats.embeddingsUsed}`);
|
||||
console.log(` - Final items: ${stats.finalSelectedItems}`);
|
||||
|
||||
// Enhanced: Include pipeline information in response
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
mode,
|
||||
taskId,
|
||||
recommendation: result.recommendation,
|
||||
query: sanitizedQuery,
|
||||
processingStats: result.processingStats // Include stats for monitoring
|
||||
processingStats: {
|
||||
...result.processingStats,
|
||||
// Add micro-task specific info
|
||||
pipelineType: 'micro-task',
|
||||
microTasksSuccessRate: stats.microTasksCompleted / (stats.microTasksCompleted + stats.microTasksFailed),
|
||||
averageTaskTime: stats.processingTimeMs / (stats.microTasksCompleted + stats.microTasksFailed)
|
||||
},
|
||||
// Enhanced: Rate limiting info for client
|
||||
rateLimitInfo: {
|
||||
remaining: microTaskLimit.remaining,
|
||||
resetTime: Date.now() + RATE_LIMIT_WINDOW
|
||||
}
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('AI query error:', error);
|
||||
console.error('[MICRO-TASK API] Pipeline error:', error);
|
||||
|
||||
// Provide more specific error messages based on error type
|
||||
// Enhanced: More specific error messages for micro-task pipeline
|
||||
if (error.message.includes('embeddings')) {
|
||||
return apiServerError.unavailable('Embeddings service error - falling back to basic processing');
|
||||
return apiServerError.unavailable('Embeddings service error - falling back to selector AI');
|
||||
} else if (error.message.includes('micro-task')) {
|
||||
return apiServerError.unavailable('Micro-task pipeline error - some analysis steps may have failed');
|
||||
} else if (error.message.includes('selector')) {
|
||||
return apiServerError.unavailable('AI selector service error');
|
||||
} else if (error.message.includes('analyzer')) {
|
||||
return apiServerError.unavailable('AI analyzer service error');
|
||||
} else if (error.message.includes('rate limit')) {
|
||||
return apiError.rateLimit('AI service rate limits exceeded due to micro-task processing');
|
||||
} else {
|
||||
return apiServerError.internal('Internal server error');
|
||||
return apiServerError.internal('Micro-task AI pipeline error');
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user