Skip to content

Commit 012cb6b

Browse files
committed
Added Anthropic & OpenAI Support
1 parent a727113 commit 012cb6b

File tree

7 files changed

+344
-43
lines changed

7 files changed

+344
-43
lines changed

next-js-app/package-lock.json

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

next-js-app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
"export": "next export"
1111
},
1212
"dependencies": {
13+
"@anthropic-ai/sdk": "^0.61.0",
1314
"@google/generative-ai": "^0.14.1",
1415
"@vercel/blob": "^1.1.1",
1516
"@vercel/kv": "^3.0.0",
1617
"next": "15.4.6",
18+
"openai": "^5.20.0",
1719
"react": "19.1.0",
1820
"react-dom": "19.1.0"
1921
},

next-js-app/src/app/api/generate/route.ts

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
import { NextRequest, NextResponse } from "next/server";
22
import { GoogleGenerativeAI } from "@google/generative-ai";
3+
import OpenAI from "openai";
4+
import Anthropic from "@anthropic-ai/sdk";
35
import fs from "fs";
46
import path from "path";
57

6-
export async function POST(req: NextRequest) {
7-
const envApiKey = process.env.GEMINI_API_KEY;
8-
let apiKeyToUse = envApiKey;
8+
const modelToProvider: { [key: string]: string } = {
9+
"gemini-2.5-flash": "Gemini",
10+
"gemini-2.5-pro": "Gemini",
11+
"gemini-2.5-flash-lite": "Gemini",
12+
"gemini-1.5-flash": "Gemini",
13+
"claude-3-7-sonnet-latest": "Claude",
14+
"claude-opus-4-1-20250805": "Claude",
15+
"claude-sonnet-4-20250514": "Claude",
16+
"claude-3-haiku-20240307": "Claude",
17+
"gpt-4o": "OpenAI",
18+
"gpt-4-turbo": "OpenAI",
19+
"gpt-3.5-turbo": "OpenAI",
20+
};
921

22+
export async function POST(req: NextRequest) {
1023
try {
1124
const {
1225
apiKey,
@@ -18,42 +31,52 @@ export async function POST(req: NextRequest) {
1831
temperature = 0.5,
1932
} = await req.json();
2033

21-
if (apiKey) {
22-
apiKeyToUse = apiKey;
34+
if (!currentPath) {
35+
return NextResponse.json(
36+
{ error: "Current path is required" },
37+
{ status: 400 }
38+
);
2339
}
2440

25-
if (!apiKeyToUse) {
41+
if (!selectedModel) {
2642
return NextResponse.json(
27-
{ error: "API key not configured" },
43+
{ error: "Model selection is required" },
2844
{ status: 400 }
2945
);
3046
}
3147

32-
if (!currentPath) {
48+
const provider = modelToProvider[selectedModel];
49+
50+
if (!provider) {
3351
return NextResponse.json(
34-
{ error: "Current path is required" },
52+
{ error: "Invalid model selected" },
3553
{ status: 400 }
3654
);
3755
}
3856

39-
if (!selectedModel) {
57+
let apiKeyToUse: string | undefined;
58+
if (apiKey) {
59+
apiKeyToUse = apiKey;
60+
} else {
61+
if (provider === "Gemini") {
62+
apiKeyToUse = process.env.GEMINI_API_KEY;
63+
} else if (provider === "OpenAI") {
64+
apiKeyToUse = process.env.OPENAI_API_KEY;
65+
} else if (provider === "Claude") {
66+
apiKeyToUse = process.env.ANTHROPIC_API_KEY;
67+
}
68+
}
69+
70+
if (!apiKeyToUse) {
4071
return NextResponse.json(
41-
{ error: "Model selection is required" },
72+
{ error: `API key for ${provider} not configured` },
4273
{ status: 400 }
4374
);
4475
}
4576

4677
const infoMdPath = path.join(process.cwd(), "info.md");
4778
const infoMdContent = fs.readFileSync(infoMdPath, "utf-8");
4879

49-
const genAI = new GoogleGenerativeAI(apiKeyToUse);
50-
const model = genAI.getGenerativeModel({
51-
model: selectedModel,
52-
generationConfig: {
53-
temperature: temperature,
54-
},
55-
});
56-
5780
const systemInstructionsPath = path.join(
5881
process.cwd(),
5982
"src",
@@ -78,7 +101,7 @@ export async function POST(req: NextRequest) {
78101
**Available Image Assets:**
79102
You can use the following images in your design. Assume they are served from the '/image-assets' path. For example, to use 'profile.jpg', the path would STRICTLY be '/image-assets/profile.jpg'.
80103
---
81-
${imageFiles.map((file) => `- ${file}`).join("\n")}---
104+
${imageFiles.map((file) => `- ${file}`).join("\n")}---
82105
`;
83106
}
84107
}
@@ -90,25 +113,74 @@ ${additionalInstructions}
90113
---`
91114
: "";
92115

93-
const prompt = promptTemplate
94-
.replace("{{infoMdContent}}", infoMdContent)
95-
.replace("{{additionalInstructions}}", additionalInstructionsText)
96-
.replace("{{imageAssets}}", imageAssetsText)
97-
.replace(/{{currentPath}}/g, currentPath)
98-
.replace("{{language}}", language);
99-
100-
const result = await model.generateContent(prompt);
101-
const response = await result.response;
102-
const text = await response.text();
116+
let generatedContent: string = "";
103117

104118
console.log(
105119
"Using model:",
106120
selectedModel,
121+
"from provider:",
122+
provider,
107123
"with temperature:",
108124
temperature
109125
);
110126

111-
return NextResponse.json({ generatedContent: text });
127+
if (provider === "Gemini") {
128+
const genAI = new GoogleGenerativeAI(apiKeyToUse);
129+
const model = genAI.getGenerativeModel({
130+
model: selectedModel,
131+
generationConfig: {
132+
temperature: temperature,
133+
},
134+
});
135+
const prompt = promptTemplate
136+
.replace("{{infoMdContent}}", infoMdContent)
137+
.replace("{{additionalInstructions}}", additionalInstructionsText)
138+
.replace("{{imageAssets}}", imageAssetsText)
139+
.replace(/{{currentPath}}/g, currentPath)
140+
.replace("{{language}}", language);
141+
const result = await model.generateContent(prompt);
142+
const response = await result.response;
143+
generatedContent = await response.text();
144+
} else if (provider === "OpenAI") {
145+
const openai = new OpenAI({ apiKey: apiKeyToUse });
146+
const systemPrompt = promptTemplate
147+
.replace("{{infoMdContent}}", "")
148+
.replace("{{additionalInstructions}}", "")
149+
.replace("{{imageAssets}}", "")
150+
.replace(/{{currentPath}}/g, currentPath)
151+
.replace("{{language}}", language);
152+
const userPrompt = `Here is the raw information about the candidate:\n---\n${infoMdContent}\n---\n${additionalInstructionsText}\n${imageAssetsText}`;
153+
const completion = await openai.chat.completions.create({
154+
model: selectedModel,
155+
temperature: temperature,
156+
messages: [
157+
{ role: "system", content: systemPrompt },
158+
{ role: "user", content: userPrompt },
159+
],
160+
});
161+
generatedContent = completion.choices[0].message.content ?? "";
162+
} else if (provider === "Claude") {
163+
const anthropic = new Anthropic({ apiKey: apiKeyToUse });
164+
const systemPrompt = promptTemplate
165+
.replace("{{infoMdContent}}", "")
166+
.replace("{{additionalInstructions}}", "")
167+
.replace("{{imageAssets}}", "")
168+
.replace(/{{currentPath}}/g, currentPath)
169+
.replace("{{language}}", language);
170+
const userPrompt = `Here is the raw information about the candidate:\n---\n${infoMdContent}\n---\n${additionalInstructionsText}\n${imageAssetsText}`;
171+
const message = await anthropic.messages.create({
172+
model: selectedModel,
173+
max_tokens: 4096,
174+
temperature: temperature,
175+
system: systemPrompt,
176+
messages: [{ role: "user", content: userPrompt }],
177+
});
178+
if (message.content[0].type === "text") {
179+
generatedContent = message.content[0].text;
180+
}
181+
}
182+
183+
return NextResponse.json({ generatedContent });
112184
} catch (error) {
113185
console.error("Error generating content:", error);
114186
return NextResponse.json(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
3+
const AnthropicIcon = ({ className }: { className?: string }) => (
4+
<svg
5+
className={className}
6+
viewBox="0 0 24 24"
7+
fill="none"
8+
xmlns="http://www.w3.org/2000/svg"
9+
>
10+
<path
11+
d="M12 2L2 7v10l10 5 10-5V7L12 2zm0 2.236L19.535 7 12 11.764 4.465 7 12 4.236zM4 9.382l8 4.571 8-4.571V15.5L12 20l-8-4.5V9.382z"
12+
fill="currentColor"
13+
/>
14+
</svg>
15+
);
16+
17+
export default AnthropicIcon;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
3+
const GeminiIcon = ({ className }: { className?: string }) => (
4+
<svg
5+
className={className}
6+
viewBox="0 0 24 24"
7+
fill="none"
8+
xmlns="http://www.w3.org/2000/svg"
9+
>
10+
<path
11+
d="M12 2L9.44 8.34L2 9.71L7.2 14.47L5.82 21.02L12 17.77L18.18 21.02L16.8 14.47L22 9.71L14.56 8.34L12 2Z"
12+
fill="currentColor"
13+
/>
14+
</svg>
15+
);
16+
17+
export default GeminiIcon;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
3+
const OpenAIIcon = ({ className }: { className?: string }) => (
4+
<svg
5+
className={className}
6+
viewBox="0 0 24 24"
7+
fill="none"
8+
xmlns="http://www.w3.org/2000/svg"
9+
>
10+
<path
11+
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8z"
12+
fill="currentColor"
13+
/>
14+
<path
15+
d="M12 6c-3.309 0-6 2.691-6 6s2.691 6 6 6 6-2.691 6-6-2.691-6-6-6zm0 10c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4z"
16+
fill="currentColor"
17+
/>
18+
</svg>
19+
);
20+
21+
export default OpenAIIcon;

0 commit comments

Comments
 (0)