harvesthealth commited on
Commit
8a55782
·
verified ·
1 Parent(s): fc051a4

Refactor: Consolidate AI providers to Blablador (4/5)

Browse files
Files changed (1) hide show
  1. app/api/analyze-edit-intent/route.ts +18 -125
app/api/analyze-edit-intent/route.ts CHANGED
@@ -1,58 +1,24 @@
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { createGroq } from '@ai-sdk/groq';
3
- import { createAnthropic } from '@ai-sdk/anthropic';
4
- import { createOpenAI } from '@ai-sdk/openai';
5
- import { createGoogleGenerativeAI } from '@ai-sdk/google';
6
  import { generateObject } from 'ai';
7
  import { z } from 'zod';
8
- // import type { FileManifest } from '@/types/file-manifest'; // Type is used implicitly through manifest parameter
9
-
10
- // Check if we're using Vercel AI Gateway
11
- const isUsingAIGateway = !!process.env.AI_GATEWAY_API_KEY;
12
- const aiGatewayBaseURL = 'https://ai-gateway.vercel.sh/v1';
13
-
14
- const groq = createGroq({
15
- apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.GROQ_API_KEY,
16
- baseURL: isUsingAIGateway ? aiGatewayBaseURL : undefined,
17
- });
18
-
19
- const anthropic = createAnthropic({
20
- apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.ANTHROPIC_API_KEY,
21
- baseURL: isUsingAIGateway ? aiGatewayBaseURL : (process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com/v1'),
22
- });
23
-
24
- const openai = createOpenAI({
25
- apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.OPENAI_API_KEY,
26
- baseURL: isUsingAIGateway ? aiGatewayBaseURL : process.env.OPENAI_BASE_URL,
27
- });
28
-
29
- const googleGenerativeAI = createGoogleGenerativeAI({
30
- apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.GEMINI_API_KEY,
31
- baseURL: isUsingAIGateway ? aiGatewayBaseURL : undefined,
32
- });
33
 
34
  // Schema for the AI's search plan - not file selection!
35
  const searchPlanSchema = z.object({
36
  editType: z.enum([
37
  'UPDATE_COMPONENT',
38
- 'ADD_FEATURE',
39
  'FIX_ISSUE',
40
  'UPDATE_STYLE',
41
  'REFACTOR',
42
  'ADD_DEPENDENCY',
43
  'REMOVE_ELEMENT'
44
  ]).describe('The type of edit being requested'),
45
-
46
  reasoning: z.string().describe('Explanation of the search strategy'),
47
-
48
  searchTerms: z.array(z.string()).describe('Specific text to search for (case-insensitive). Be VERY specific - exact button text, class names, etc.'),
49
-
50
- regexPatterns: z.array(z.string()).optional().describe('Regex patterns for finding code structures (e.g., "className=[\\"\\\'].*header.*[\\"\\\']")'),
51
-
52
  fileTypesToSearch: z.array(z.string()).default(['.jsx', '.tsx', '.js', '.ts']).describe('File extensions to search'),
53
-
54
  expectedMatches: z.number().min(1).max(10).default(1).describe('Expected number of matches (helps validate search worked)'),
55
-
56
  fallbackSearch: z.object({
57
  terms: z.array(z.string()),
58
  patterns: z.array(z.string()).optional()
@@ -61,37 +27,33 @@ const searchPlanSchema = z.object({
61
 
62
  export async function POST(request: NextRequest) {
63
  try {
64
- const { prompt, manifest, model = 'openai/gpt-oss-20b' } = await request.json();
65
-
66
  console.log('[analyze-edit-intent] Request received');
67
  console.log('[analyze-edit-intent] Prompt:', prompt);
68
- console.log('[analyze-edit-intent] Model:', model);
69
  console.log('[analyze-edit-intent] Manifest files count:', manifest?.files ? Object.keys(manifest.files).length : 0);
70
-
71
  if (!prompt || !manifest) {
72
  return NextResponse.json({
73
  error: 'prompt and manifest are required'
74
  }, { status: 400 });
75
  }
76
-
77
- // Create a summary of available files for the AI
78
  const validFiles = Object.entries(manifest.files as Record<string, any>)
79
  .filter(([path]) => {
80
- // Filter out invalid paths
81
  return path.includes('.') && !path.match(/\/\d+$/);
82
  });
83
-
84
  const fileSummary = validFiles
85
  .map(([path, info]: [string, any]) => {
86
  const componentName = info.componentInfo?.name || path.split('/').pop();
87
- // const hasImports = info.imports?.length > 0; // Kept for future use
88
  const childComponents = info.componentInfo?.childComponents?.join(', ') || 'none';
89
  return `- ${path} (${componentName}, renders: ${childComponents})`;
90
  })
91
  .join('\n');
92
-
93
  console.log('[analyze-edit-intent] Valid files found:', validFiles.length);
94
-
95
  if (validFiles.length === 0) {
96
  console.error('[analyze-edit-intent] No valid files found in manifest');
97
  return NextResponse.json({
@@ -99,32 +61,16 @@ export async function POST(request: NextRequest) {
99
  error: 'No valid files found in manifest'
100
  }, { status: 400 });
101
  }
102
-
103
  console.log('[analyze-edit-intent] Analyzing prompt:', prompt);
104
  console.log('[analyze-edit-intent] File summary preview:', fileSummary.split('\n').slice(0, 5).join('\n'));
105
-
106
- // Select the appropriate AI model based on the request
107
- let aiModel;
108
- if (model.startsWith('anthropic/')) {
109
- aiModel = anthropic(model.replace('anthropic/', ''));
110
- } else if (model.startsWith('openai/')) {
111
- if (model.includes('gpt-oss')) {
112
- aiModel = groq(model);
113
- } else {
114
- aiModel = openai(model.replace('openai/', ''));
115
- }
116
- } else if (model.startsWith('google/')) {
117
- aiModel = googleGenerativeAI(model.replace('google/', ''));
118
- } else {
119
- // Default to groq if model format is unclear
120
- aiModel = groq(model);
121
- }
122
-
123
- console.log('[analyze-edit-intent] Using AI model:', model);
124
-
125
- // Use AI to create a search plan
126
  const result = await generateObject({
127
- model: aiModel,
128
  schema: searchPlanSchema,
129
  messages: [
130
  {
@@ -134,57 +80,4 @@ export async function POST(request: NextRequest) {
134
  DO NOT GUESS which files to edit. Instead, provide specific search terms that will locate the code.
135
 
136
  SEARCH STRATEGY RULES:
137
- 1. For text changes (e.g., "change 'Start Deploying' to 'Go Now'"):
138
- - Search for the EXACT text: "Start Deploying"
139
-
140
- 2. For style changes (e.g., "make header black"):
141
- - Search for component names: "Header", "<header"
142
- - Search for class names: "header", "navbar"
143
- - Search for className attributes containing relevant words
144
-
145
- 3. For removing elements (e.g., "remove the deploy button"):
146
- - Search for the button text or aria-label
147
- - Search for relevant IDs or data-testids
148
-
149
- 4. For navigation/header issues:
150
- - Search for: "navigation", "nav", "Header", "navbar"
151
- - Look for Link components or href attributes
152
-
153
- 5. Be SPECIFIC:
154
- - Use exact capitalization for user-visible text
155
- - Include multiple search terms for redundancy
156
- - Add regex patterns for structural searches
157
-
158
- Current project structure for context:
159
- ${fileSummary}`
160
- },
161
- {
162
- role: 'user',
163
- content: `User request: "${prompt}"
164
-
165
- Create a search plan to find the exact code that needs to be modified. Include specific search terms and patterns.`
166
- }
167
- ]
168
- });
169
-
170
- console.log('[analyze-edit-intent] Search plan created:', {
171
- editType: result.object.editType,
172
- searchTerms: result.object.searchTerms,
173
- patterns: result.object.regexPatterns?.length || 0,
174
- reasoning: result.object.reasoning
175
- });
176
-
177
- // Return the search plan, not file matches
178
- return NextResponse.json({
179
- success: true,
180
- searchPlan: result.object
181
- });
182
-
183
- } catch (error) {
184
- console.error('[analyze-edit-intent] Error:', error);
185
- return NextResponse.json({
186
- success: false,
187
- error: (error as Error).message
188
- }, { status: 500 });
189
- }
190
- }
 
1
  import { NextRequest, NextResponse } from 'next/server';
 
 
 
 
2
  import { generateObject } from 'ai';
3
  import { z } from 'zod';
4
+ import getProviderForModel from '@/lib/ai/provider-manager';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  // Schema for the AI's search plan - not file selection!
7
  const searchPlanSchema = z.object({
8
  editType: z.enum([
9
  'UPDATE_COMPONENT',
10
+ 'ADD_FEATURE',
11
  'FIX_ISSUE',
12
  'UPDATE_STYLE',
13
  'REFACTOR',
14
  'ADD_DEPENDENCY',
15
  'REMOVE_ELEMENT'
16
  ]).describe('The type of edit being requested'),
 
17
  reasoning: z.string().describe('Explanation of the search strategy'),
 
18
  searchTerms: z.array(z.string()).describe('Specific text to search for (case-insensitive). Be VERY specific - exact button text, class names, etc.'),
19
+ regexPatterns: z.array(z.string()).optional().describe('Regex patterns for finding code structures (e.g., "className=[\"\\\'].*header.*[\"\\]")'),
 
 
20
  fileTypesToSearch: z.array(z.string()).default(['.jsx', '.tsx', '.js', '.ts']).describe('File extensions to search'),
 
21
  expectedMatches: z.number().min(1).max(10).default(1).describe('Expected number of matches (helps validate search worked)'),
 
22
  fallbackSearch: z.object({
23
  terms: z.array(z.string()),
24
  patterns: z.array(z.string()).optional()
 
27
 
28
  export async function POST(request: NextRequest) {
29
  try {
30
+ const { prompt, manifest } = await request.json();
31
+
32
  console.log('[analyze-edit-intent] Request received');
33
  console.log('[analyze-edit-intent] Prompt:', prompt);
 
34
  console.log('[analyze-edit-intent] Manifest files count:', manifest?.files ? Object.keys(manifest.files).length : 0);
35
+
36
  if (!prompt || !manifest) {
37
  return NextResponse.json({
38
  error: 'prompt and manifest are required'
39
  }, { status: 400 });
40
  }
41
+
 
42
  const validFiles = Object.entries(manifest.files as Record<string, any>)
43
  .filter(([path]) => {
 
44
  return path.includes('.') && !path.match(/\/\d+$/);
45
  });
46
+
47
  const fileSummary = validFiles
48
  .map(([path, info]: [string, any]) => {
49
  const componentName = info.componentInfo?.name || path.split('/').pop();
 
50
  const childComponents = info.componentInfo?.childComponents?.join(', ') || 'none';
51
  return `- ${path} (${componentName}, renders: ${childComponents})`;
52
  })
53
  .join('\n');
54
+
55
  console.log('[analyze-edit-intent] Valid files found:', validFiles.length);
56
+
57
  if (validFiles.length === 0) {
58
  console.error('[analyze-edit-intent] No valid files found in manifest');
59
  return NextResponse.json({
 
61
  error: 'No valid files found in manifest'
62
  }, { status: 400 });
63
  }
64
+
65
  console.log('[analyze-edit-intent] Analyzing prompt:', prompt);
66
  console.log('[analyze-edit-intent] File summary preview:', fileSummary.split('\n').slice(0, 5).join('\n'));
67
+
68
+ const { client, actualModel } = getProviderForModel('text');
69
+
70
+ console.log('[analyze-edit-intent] Using AI model:', actualModel);
71
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  const result = await generateObject({
73
+ model: client(actualModel),
74
  schema: searchPlanSchema,
75
  messages: [
76
  {
 
80
  DO NOT GUESS which files to edit. Instead, provide specific search terms that will locate the code.
81
 
82
  SEARCH STRATEGY RULES:
83
+ 1. For text changes (e.g.,