Khrisna's picture
Update router/api.js
8ec3d53 verified
raw
history blame
8.68 kB
import express from 'express';
import axios from 'axios';
import crypto from 'crypto';
import FormData from 'form-data'; // Import modul form-data untuk menghandle multipart
import Groq from 'groq-sdk';
import bytes from 'bytes';
import { feloAI } from '../lib/feloAI.js';
import { toAnime } from '../lib/toanime.js';
import { convertWebpToPng } from '../lib/converter.js';
const APIrouter = express.Router();
const ISauthenticate = async (req, res, next) => {
try {
// Contoh sederhana: menggunakan menit UTC sebagai secret
const secretResponse = new Date().getUTCMinutes().toString();
const generatedApiKey = generateApiKey(secretResponse);
console.log(generatedApiKey);
console.log(req.headers);
const authHeader = req.headers['authorization'];
if (!authHeader || authHeader !== `Bearer ${generatedApiKey}`) {
return res.status(403).json({ success: false, message: 'Unauthorized' });
}
next();
} catch (error) {
console.error('Authentication error:', error);
return res.status(500).json({ success: false, message: 'Internal Server Error' });
}
};
// Inisialisasi Groq SDK
const client = new Groq({
apiKey: process.env.GROQ_API_KEY || "",
dangerouslyAllowBrowser: true,
});
APIrouter.get('/', (req, res) => {
res.send('Hello World');
});
APIrouter.get('/msecret', (req, res) => {
const secret = new Date().getUTCMinutes().toString();
res.json({ msec: secret });
});
APIrouter.post('/gpt/completions', ISauthenticate, async (req, res) => {
const {
messages,
model,
temperature,
max_completion_tokens,
top_p,
stream,
stop,
searchMode
} = req.body;
try {
// Set header untuk streaming (Server-Sent Events)
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Mode tanpa search (normal GPT response stream)
if (searchMode === false) {
const chatStream = await client.chat.completions.create({
messages,
model,
temperature,
max_completion_tokens,
top_p,
stream,
stop,
});
// Kirim setiap chunk stream ke client
for await (const chunk of chatStream) {
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
}
res.write('data: [DONE]\n\n');
return res.end();
}
// Mode dengan search (tool use)
else if (searchMode === true) {
// Definisi fungsi searchEngine
const searchEngine = async (query, langcode = 'en-US') => {
try {
console.log("searching...");
console.log("query:", query);
console.log("lang:", langcode);
const searchResult = await feloAI(query, langcode);
// Susun hasil pencarian
let result = { answer: "", source: [] };
result.answer = searchResult.answer;
for (let i = 0; i < 5; i++) {
result.source.push({
title: searchResult.source[i].title,
link: searchResult.source[i].link,
snippet: searchResult.source[i].snippet
});
}
return result;
} catch (error) {
console.error(error);
return null;
}
};
// Definisi tool untuk searchEngine
const tools = [
{
type: "function",
function: {
name: "searchEngine",
description:
"Mencari informasi secara real-time dengan search engine berdasarkan query dan target daerah.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "Query pencarian untuk search engine."
},
langcode: {
type: "string",
description:
"Kode atau nama daerah target (misalnya 'id-MM', 'en-US', atau lainnya)."
}
},
required: ["query", "langcode"]
}
}
}
];
// Panggilan awal ke Groq untuk mendapatkan tool call
const response = await client.chat.completions.create({
model: model,
messages,
stream: false,
tools,
tool_choice: "auto",
max_completion_tokens: 4096
});
const responseMessage = response.choices[0].message;
console.log("Response message:", JSON.stringify(responseMessage, null, 2));
// Hapus properti yang tidak didukung
if (responseMessage.reasoning) {
delete responseMessage.reasoning;
}
const toolCalls = responseMessage.tool_calls || [];
if (toolCalls.length > 0) {
const availableFunctions = { searchEngine };
// Tambahkan respons awal ke array pesan
messages.push(responseMessage);
// Eksekusi setiap tool call
for (const toolCall of toolCalls) {
const functionName = toolCall.function.name;
const functionToCall = availableFunctions[functionName];
const functionArgs = JSON.parse(toolCall.function.arguments);
const functionResponse = await functionToCall(
functionArgs.query,
functionArgs.langcode
);
messages.push({
tool_call_id: toolCall.id,
role: "tool",
name: functionName,
content: JSON.stringify(functionResponse)
});
}
}
// Panggilan kedua dengan opsi streaming
const secondResponseStream = await client.chat.completions.create({
model: model,
messages,
stream,
});
let finalContent = "";
try {
for await (const chunk of secondResponseStream) {
// Ambil chunk respons (sesuaikan dengan struktur output model)
const contentChunk =
(chunk.choices && chunk.choices[0].delta && chunk.choices[0].delta.content) || "";
finalContent += contentChunk;
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
}
} catch (err) {
console.error("Error processing stream:", err);
}
res.write('data: [DONE]\n\n');
res.end();
}
} catch (error) {
console.error('GPT completion error:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
APIrouter.get('/gpt/modellist', ISauthenticate, async (req, res) => {
try {
const models = await client.models.list();
res.json(models);
} catch (error) {
console.error('GPT completion error:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
APIrouter.post('/toanime', async (req, res) => {
try {
console.log(req.body)
const { images } = req.body
if (!images) return res.json({ success: false, message: 'Required an images!' })
if (/^(https?|http):\/\//i.test(images)) {
const data_img = await axios.request({
method: "GET",
url: images,
responseType: "arraybuffer"
})
const response = await toAnime({ imgBuffer: data_img.data });
//const type_img = await fileTypeFromBuffer(response)
//res.setHeader('Content-Type', type_img.mime)
res.json({
status: true,
data: response
})
} else if (images && typeof images == 'string' && isBase64(images)) {
const response = await toAnime({ imgBuffer: Buffer.from(images, "base64") });
//const converted = await convertWebpToPng(response);
//const type_img = await fileTypeFromBuffer(response)
//res.setHeader('Content-Type', type_img.mime)
res.json({
status: true,
data: response
})
} else {
res.json({
success: false, message: 'No url or base64 detected!!'
})
}
} catch (e) {
console.log(e)
e = String(e)
res.json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e })
}
})
export default APIrouter;
function generateApiKey(secret) {
// Ambil menit saat ini sebagai nilai integer
const currentMinute = Math.floor(Date.now() / 60000).toString();
// Buat HMAC menggunakan algoritma SHA256 dan secret key
const hmac = crypto.createHmac('sha256', secret);
hmac.update(currentMinute);
// Kembalikan hash dalam format hexadecimal
return hmac.digest('hex');
};
function formatSize(num) {
return bytes(+num || 0, { unitSeparator: ' ' })
}
function isBase64(str) {
try {
return btoa(atob(str)) === str
} catch {
return false
}
}