added fast apply

if isEdit === true & Morph API Key is in .env:
- Morph Fast Apply is activated.
This commit is contained in:
Bekbol Bolatov
2025-09-11 18:24:27 +05:00
parent 280c177619
commit 2327442b89
5 changed files with 351 additions and 7 deletions
+70 -3
View File
@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from 'next/server';
import { parseMorphEdits, applyMorphEditToFile } from '@/lib/morph-fast-apply';
import type { SandboxState } from '@/types/sandbox';
import type { ConversationState } from '@/types/conversation';
@@ -144,6 +145,12 @@ export async function POST(request: NextRequest) {
// Parse the AI response
const parsed = parseAIResponse(response);
const morphEnabled = Boolean(isEdit && process.env.MORPH_API_KEY);
const morphEdits = morphEnabled ? parseMorphEdits(response) : [];
console.log('[apply-ai-code] Morph Fast Apply mode:', morphEnabled);
if (morphEnabled) {
console.log('[apply-ai-code] Morph edits found:', morphEdits.length);
}
// Initialize existingFiles if not already
if (!global.existingFiles) {
@@ -172,6 +179,14 @@ export async function POST(request: NextRequest) {
console.log('[apply-ai-code] Is edit mode:', isEdit);
console.log('[apply-ai-code] Files to write:', parsed.files.map(f => f.path));
console.log('[apply-ai-code] Existing files:', Array.from(global.existingFiles));
if (morphEnabled) {
console.log('[apply-ai-code] Morph Fast Apply enabled');
if (morphEdits.length > 0) {
console.log('[apply-ai-code] Parsed Morph edits:', morphEdits.map(e => e.targetFile));
} else {
console.log('[apply-ai-code] No <edit> blocks found in response');
}
}
const results = {
filesCreated: [] as string[],
@@ -296,9 +311,46 @@ export async function POST(request: NextRequest) {
}
}
// Attempt Morph Fast Apply for edits before file creation
const morphUpdatedPaths = new Set<string>();
if (morphEnabled && morphEdits.length > 0) {
if (!global.activeSandbox) {
console.warn('[apply-ai-code] Morph edits found but no active sandbox; skipping Morph application');
} else {
console.log(`[apply-ai-code] Applying ${morphEdits.length} fast edits via Morph...`);
for (const edit of morphEdits) {
try {
const result = await applyMorphEditToFile({
sandbox: global.activeSandbox,
targetPath: edit.targetFile,
instructions: edit.instructions,
updateSnippet: edit.update
});
if (result.success && result.normalizedPath) {
morphUpdatedPaths.add(result.normalizedPath);
results.filesUpdated.push(result.normalizedPath);
console.log('[apply-ai-code] Morph applied to', result.normalizedPath);
} else {
const msg = result.error || 'Unknown Morph error';
console.error('[apply-ai-code] Morph apply failed:', msg);
results.errors.push(`Morph apply failed for ${edit.targetFile}: ${msg}`);
}
} catch (e) {
console.error('[apply-ai-code] Morph apply exception:', e);
results.errors.push(`Morph apply exception for ${edit.targetFile}: ${(e as Error).message}`);
}
}
}
}
if (morphEnabled && morphEdits.length === 0) {
console.warn('[apply-ai-code] Morph enabled but no <edit> blocks found; falling back to full-file flow');
}
// Filter out config files that shouldn't be created
const configFiles = ['tailwind.config.js', 'vite.config.js', 'package.json', 'package-lock.json', 'tsconfig.json', 'postcss.config.js'];
const filteredFiles = parsed.files.filter(file => {
let filteredFiles = parsed.files.filter(file => {
const fileName = file.path.split('/').pop() || '';
if (configFiles.includes(fileName)) {
console.warn(`[apply-ai-code] Skipping config file: ${file.path} - already exists in template`);
@@ -306,6 +358,21 @@ export async function POST(request: NextRequest) {
}
return true;
});
// Avoid overwriting files already updated by Morph
if (morphUpdatedPaths.size > 0) {
filteredFiles = filteredFiles.filter(file => {
let normalizedPath = file.path.startsWith('/') ? file.path.slice(1) : file.path;
const fileName = normalizedPath.split('/').pop() || '';
if (!normalizedPath.startsWith('src/') &&
!normalizedPath.startsWith('public/') &&
normalizedPath !== 'index.html' &&
!configFiles.includes(fileName)) {
normalizedPath = 'src/' + normalizedPath;
}
return !morphUpdatedPaths.has(normalizedPath);
});
}
// Create or update files AFTER package installation
for (const file of filteredFiles) {
@@ -354,7 +421,7 @@ export async function POST(request: NextRequest) {
} catch (writeError) {
console.error(`[apply-ai-code] E2B file write error:`, writeError);
throw writeError;
throw writeError as Error;
}
@@ -491,7 +558,7 @@ with open(file_path, 'w') as f:
print(f"Auto-generated: {file_path}")
`);
results.filesCreated.push('src/index.css (with Tailwind)');
} catch (error) {
} catch {
results.errors.push('Failed to create index.css with Tailwind');
}
}