Spaces:
Running
Running
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 | |
} | |
} |