TechScout/dashboard/app/api/discover/route.ts

121 lines
3.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { spawn } from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
export async function POST(request: NextRequest) {
const { query } = await request.json();
if (!query) {
return NextResponse.json({ error: 'Query is required' }, { status: 400 });
}
// Create a streaming response
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
// Send initial status
controller.enqueue(encoder.encode(JSON.stringify({ status: 'Starting discovery...' }) + '\n'));
// Path to Python script
const techscoutDir = path.resolve(process.cwd(), '..');
const pythonScript = path.join(techscoutDir, 'run_discovery.py');
// Create a temporary Python script to run the discovery
const scriptContent = `
import sys
import json
sys.path.insert(0, r'${techscoutDir}')
from techscout.pipeline.discovery import DiscoveryPipeline
def main():
pipeline = DiscoveryPipeline(model='llama3:8b')
# Status updates via print
print(json.dumps({"status": "Decomposing capability gap..."}), flush=True)
result = pipeline.discover(
capability_gap="""${query.replace(/"/g, '\\"')}""",
max_results=50,
use_llm_scoring=True
)
# Output result
print(json.dumps({"result": result.to_dict()}), flush=True)
if __name__ == '__main__':
main()
`;
// Write temporary script
fs.writeFileSync(pythonScript, scriptContent);
try {
const python = spawn('python', [pythonScript], {
cwd: techscoutDir,
env: { ...process.env, PYTHONUNBUFFERED: '1' }
});
python.stdout.on('data', (data: Buffer) => {
const lines = data.toString().split('\n').filter(Boolean);
for (const line of lines) {
try {
// Try to parse as JSON
JSON.parse(line);
controller.enqueue(encoder.encode(line + '\n'));
} catch {
// Not JSON, send as status
controller.enqueue(encoder.encode(JSON.stringify({ status: line }) + '\n'));
}
}
});
python.stderr.on('data', (data: Buffer) => {
const message = data.toString();
// Check if it's a progress message (from logging)
if (message.includes('INFO')) {
const match = message.match(/- (.+)$/);
if (match) {
controller.enqueue(encoder.encode(JSON.stringify({ status: match[1] }) + '\n'));
}
}
});
await new Promise<void>((resolve, reject) => {
python.on('close', (code: number) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Python process exited with code ${code}`));
}
});
python.on('error', reject);
});
} catch (error) {
controller.enqueue(encoder.encode(JSON.stringify({
error: `Discovery failed: ${error}`
}) + '\n'));
} finally {
// Cleanup
try {
fs.unlinkSync(pythonScript);
} catch {
// Ignore cleanup errors
}
controller.close();
}
}
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}