Reputation: 11
I am working with Next.js 13.4.19
and I want to try OpenAI API. I have my API routes in good structure. When I hit the API route http://localhost:3000/api/course/createChapters
for POST
with:
{
'title':'calculus',
'units':['algebra','differentiation','integration']
}
I get the following error:
- error TypeError: Cannot read properties of undefined (reading 'headers')
at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/next@13.4.19_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/server/future/route-modules/app-route/module.js:266:61)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
gpt.ts
file:import OpenAI from "openai";
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
interface OutputFormat {
[key: string]: string | string[] | OutputFormat;
}
export async function strict_output(
system_prompt: string,
user_prompt: string | string[],
output_format: OutputFormat,
default_category: string = "",
output_value_only: boolean = false,
model: string = "gpt-3.5-turbo",
temperature: number = 1,
num_tries: number = 3,
verbose: boolean = false
) {
const list_input: boolean = Array.isArray(user_prompt);
const dynamic_elements: boolean = /<.*?>/.test(JSON.stringify(output_format));
const list_output: boolean = /\[.*?\]/.test(JSON.stringify(output_format));
let error_msg: string = "";
for (let i = 0; i < num_tries; i++) {
let output_format_prompt: string = `\nYou are to output ${
list_output && "an array of objects in"
} the following in json format: ${JSON.stringify(
output_format
)}. \nDo not put quotation marks or escape character \\ in the output fields.`;
if (list_output) {
output_format_prompt += `\nIf output field is a list, classify output into the best element of the list.`;
}
if (dynamic_elements) {
output_format_prompt += `\nAny text enclosed by < and > indicates you must generate content to replace it. Example input: Go to <location>, Example output: Go to the garden\nAny output key containing < and > indicates you must generate the key name to replace it. Example input: {'<location>': 'description of location'}, Example output: {school: a place for education}`;
}
if (list_input) {
output_format_prompt += `\nGenerate an array of json, one json for each input element.`;
}
const response = await openai.chat.completions.create({
temperature: temperature,
model: model,
messages: [
{
role: "system",
content: system_prompt + output_format_prompt + error_msg,
},
{ role: "user", content: user_prompt.toString() },
],
});
let res: string =
response.choices[0].message?.content?.replace(/'/g, '"') ?? "";
res = res.replace(/(\w)"(\w)/g, "$1'$2");
if (verbose) {
console.log(
"System prompt:",
system_prompt + output_format_prompt + error_msg
);
console.log("\nUser prompt:", user_prompt);
console.log("\nGPT response:", res);
}
try {
let output: any = JSON.parse(res);
if (list_input) {
if (!Array.isArray(output)) {
throw new Error("Output format not in an array of json");
}
} else {
output = [output];
}
for (let index = 0; index < output.length; index++) {
for (const key in output_format) {
if (/<.*?>/.test(key)) {
continue;
}
if (!(key in output[index])) {
throw new Error(`${key} not in json output`);
}
if (Array.isArray(output_format[key])) {
const choices = output_format[key] as string[];
if (Array.isArray(output[index][key])) {
output[index][key] = output[index][key][0];
}
if (!choices.includes(output[index][key]) && default_category) {
output[index][key] = default_category;
}
if (output[index][key].includes(":")) {
output[index][key] = output[index][key].split(":")[0];
}
}
}
if (output_value_only) {
output[index] = Object.values(output[index]);
if (output[index].length === 1) {
output[index] = output[index][0];
}
}
}
return list_input ? output : output[0];
} catch (e) {
error_msg = `\n\nResult: ${res}\n\nError message: ${e}`;
console.log("An exception occurred:", e);
console.log("Current invalid json format ", res);
}
}
return [];
}
route.ts
fileimport { strict_output } from "@/lib/gpt";
import { createChaptersSchema } from "@/validators/course";
import { NextResponse } from "next/server";
import { ZodError } from "zod";
export const POST = async (req: Request) => {
try {
const body = await req.json();
const { title, units } = createChaptersSchema.parse(body);
type outptUnits = {
title: string;
chapters: {
youtube_search_query: string;
chapter_title: string;
}[];
};
let output_units: outptUnits = await strict_output(
"You are an AI capable of curating course content, coming up with relevant chapter titles, and finding relevant youtube videos for each chapter",
new Array(units.length).fill(
`It is your job to create a course about ${title}. The user has requested to create chapters for each of the units. Then, for each chapter, provide a detailed youtube search query that can be used to find an informative educationalvideo for each chapter. Each query should give an educational informative course in youtube.`
),
{
title: "title of the unit",
chapters:
"an array of chapters, each chapter should have a youtube_search_query and a chapter_title key in the JSON object",
}
);
console.log(output_units);
return NextResponse.json(output_units);
} catch (error) {
if (error instanceof ZodError) {
return new NextResponse("Invalid Body", { status: 400 });
}
}
};
model Course {
id String @id @default(cuid())
name String
image String
units Unit[]
}
model Unit {
id String @id @default(cuid())
name String
chapters Chapter[]
courseId String
course Course @relation(fields: [courseId], references: [id])
@@index([courseId], name:"courseId")
}
model Chapter {
id String @id @default(cuid())
unitId String
name String
youtubeSearchQuery String
videoId String?
summary String? @db.VarChar(3000)
unit Unit @relation(fields: [unitId], references: [id])
questions Question[]
@@index([unitId], name:"unitId")
}
model Question {
id String @id @default(cuid())
chapterId String
questionId String
chapter Chapter @relation(fields: [chapterId], references: [id])
question String @db.VarChar(3000)
options String @db.VarChar(3000)
answer String @db.VarChar(3000)
@@index([chapterId], name:"chapterId")
}
My folder structure is as follows:
My Insomnia response looks like this:
I tried to hit the API with Insomnia REST but I got no response.
I was expecting the following to happen:
Upvotes: 1
Views: 5675
Reputation: 1
i have the same problem in my next.js ecommerce web app , solve that with install next-auth beta version
Upvotes: 0
Reputation: 61
I also faced the same issue. The problem is that the chat completion request to OpenAI was not going through due to error status code 429 "Too many requests".
I had 0 credits in my OpenAI account, so the API was not letting the request through. I created a new account and got $5 free credits. Generated an API Key using this account and used it in the app.
Was able to solve this issue and the API request got through and got a successful response.
Hope it helps!
Upvotes: 1
Reputation: 1
Check if your route.ts is in course/createChapters directory rather than the course/ directory, a similar error happened to me recently when my api page was under the wrong folder
Upvotes: 0
Reputation: 588
This error happens when your route handler does not return a response.
I am not sure but I suspect that there is an error thrown in your route-handler code that ends up in the catch block.
} catch (error) {
if (error instanceof ZodError) {
return new NextResponse("Invalid Body", { status: 400 });
}
//TODO: Handle other error cases
}
From looking at your catch block, it is only returning a response when the error is an instance of instance of ZodError
.
Try to return a 500 or some other HTTP response in your catch block for other kinds of errors. I recommend logging that error in your dev environment to see it.
} catch (error) {
if (error instanceof ZodError) {
return new NextResponse("Invalid Body", { status: 400 });
}
return new NextResponse("Internal server error", { status: 500 });
}
Upvotes: 0