File size: 4,669 Bytes
1262001
 
 
 
 
 
e6b89d5
609fcb1
32d3b13
1262001
32d3b13
 
1262001
 
 
 
 
 
e6b89d5
1262001
 
 
 
 
 
 
 
 
 
 
 
 
e6b89d5
1262001
e6b89d5
1262001
e6b89d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1262001
e6b89d5
 
1262001
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609fcb1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { NextRequest, NextResponse } from "next/server";
import { generateText } from "ai";
import { getProviderForModel } from "@/lib/ai/provider-manager";
import { extractFilesFromAI, sanitizeBranchName } from "@/lib/ai-utils";
import { pushToGitHub } from "@/lib/github";
import { appConfig } from "@/config/app.config";
import { fetchArxivPaper } from "@/lib/arxiv";

export async function GET() {
  return NextResponse.json({ message: "Submit job endpoint is active. Use POST to submit a paper." });
}

export async function POST(request: NextRequest) {
  try {
    const { 
      arxivUrl, 
      paperContent, 
      repo, 
      paperName: providedPaperName,
      model = appConfig.ai.defaultModel 
    } = await request.json();

    if (!repo) {
      return NextResponse.json({ error: "Target GitHub repository is required (e.g. 'owner/repo')" }, { status: 400 });
    }

    const githubToken = process.env.PERSONAL_ACCESS_TOKEN;
    if (!githubToken) {
      return NextResponse.json({ error: "GitHub PERSONAL_ACCESS_TOKEN not configured in environment" }, { status: 500 });
    }

    let finalPaperContent = paperContent || "";
    let paperName = providedPaperName || "paper-to-code";

    // 1. Get paper content from Arxiv API if URL or query is provided
    if (arxivUrl && !paperContent) {
      console.log(`[submit-job] Fetching Arxiv paper: ${arxivUrl}`);
      const paper = await fetchArxivPaper(arxivUrl);
      if (paper) {
        finalPaperContent = `Title: ${paper.title}\n\nAuthors: ${paper.authors.join(', ')}\n\nSummary: ${paper.summary}`;
        paperName = paper.title;
        console.log(`[submit-job] Successfully fetched: ${paper.title}`);
      } else {
        console.warn(`[submit-job] Arxiv API failed for ${arxivUrl}, falling back to scraping`);
        // Fallback to scrape if API fails
        try {
          const scrapeResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/scrape-website`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ url: arxivUrl })
          });
          
          if (scrapeResponse.ok) {
            const scrapeData = await scrapeResponse.json();
            if (scrapeData.success) {
              finalPaperContent = scrapeData.data.content;
            }
          }
        } catch (error) {
          console.error(`[submit-job] Error calling scrape API:`, error);
        }
      }
    }

    if (!finalPaperContent) {
      return NextResponse.json({ error: "No paper content provided or found at URL" }, { status: 400 });
    }

    // 2. Generate Code from Paper
    console.log(`[submit-job] Generating code for: ${paperName}`);
    const { client, actualModel } = getProviderForModel(model);

    const systemPrompt = `You are an expert React developer. Your task is to transform the provided research paper content into a functional React application using Vite and Tailwind CSS.
    
The application should:
1. Visualize the core concepts or findings of the paper.
2. Provide interactive components if applicable (e.g., simulators, calculators, data explorers).
3. Have a professional, clean UI.
4. Include a detailed "About" section summarizing the paper.

Output format:
Generate multiple files using the <file path="..."> format.
Always start with src/index.css, then src/App.jsx, then components.
Do NOT include tailwind.config.js or vite.config.js.

${finalPaperContent.substring(0, 10000)} // Truncated paper content for prompt`;

    const { text: generatedContent } = await generateText({
      model: client(actualModel),
      system: "You transform research papers into interactive React code.",
      prompt: systemPrompt,
    });

    // 3. Extract Files
    const files = extractFilesFromAI(generatedContent);
    if (files.length === 0) {
      return NextResponse.json({ 
        error: "AI failed to generate code in the expected format",
        rawResponse: generatedContent.substring(0, 500)
      }, { status: 500 });
    }

    // 4. Push to GitHub
    const branchName = sanitizeBranchName(paperName);
    const githubResult = await pushToGitHub(repo, branchName, files, githubToken);

    return NextResponse.json({
      success: true,
      message: `Successfully transformed paper to code and pushed to GitHub`,
      githubUrl: githubResult.url,
      branch: branchName,
      filesCount: files.length
    });

  } catch (error: any) {
    console.error("[submit-job] Error:", error);
    return NextResponse.json({ 
      success: false, 
      error: error.message || "An unexpected error occurred" 
    }, { status: 500 });
  }
}