-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathroute.ts
141 lines (125 loc) · 3.69 KB
/
route.ts
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import {
HarmBlockThreshold,
HarmCategory,
VertexAI,
} from "@google-cloud/vertexai";
import { StreamingTextResponse } from "ai";
import { NextResponse } from "next/server";
const credentials = JSON.parse(
Buffer.from(process.env.GOOGLE_SERVICE_KEY || "", "base64").toString()
);
// // Initialize Vertex with your Cloud project and location
const vertex_ai = new VertexAI({
project: "teak-flash-361520",
location: "europe-west3",
googleAuthOptions: {
credentials,
},
});
const model = "gemini-1.5-pro-preview-0409";
// Instantiate the models
const generativeModel = vertex_ai.preview.getGenerativeModel({
model: model,
generationConfig: {
maxOutputTokens: 8192,
temperature: 1,
topP: 0.95,
},
safetySettings: [
{
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
],
});
function iteratorToStream(iterator: any) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next();
if (done || !value) {
controller.close();
} else {
const data = value.candidates[0].content.parts[0].text;
// controller.enqueue(`data: ${data}\n\n`);
controller.enqueue(data);
}
},
});
}
export async function POST(req: Request) {
const formData = await req.formData();
const files = formData.getAll("files") as File[];
const notes = formData.get("notes");
const totalQuizQuestions = formData.get("quizCount");
const difficulty = formData.get("difficulty");
const topic = formData.get("topic");
if (files.length < 1 && !notes) {
return new NextResponse("Please provide either a file or notes", {
status: 400,
});
}
const text1 = {
text: `You are an all-rounder tutor with professional expertise in different fields. You are to generate a list of quiz questions from the document(s) with a difficutly of ${
difficulty || "Easy"
}.`,
};
const text2 = {
text: `You response should be in JSON as an array of the object below. Respond with ${
totalQuizQuestions || 5
} different questions.
{
\"id\": 1,
\"question\": \"\",
\"description\": \"\",
\"options\": {
\"a\": \"\",
\"b\": \"\",
\"c\": \"\",
\"d\": \"\"
},
\"answer\": \"\",
}`,
};
const filesBase64 = await Promise.all(
files.map(async (file) => {
const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
// return "data:" + file.type + ";base64," + buffer.toString("base64");
return buffer.toString("base64");
})
);
const filesData = filesBase64.map((b64, i) => ({
inlineData: {
mimeType: files[i].type,
data: b64,
},
}));
const data =
files.length > 0 ? filesData : [{ text: notes?.toString() || "No notes" }];
const body = {
contents: [{ role: "user", parts: [text1, ...data, text2] }],
};
const resp = await generativeModel.generateContentStream(body);
// Convert the response into a friendly text-stream
const stream = iteratorToStream(resp.stream);
return new StreamingTextResponse(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"Transfer-Encoding": "chunked",
},
});
}