Gpt 4.1 Nano
Udah Gitu Aja
#!/usr/bin/env node
/**
* ============================================================
* Overchat.ai — Chat Scraper (auto SSE/JSON)
* Endpoint: https://api.overchat.ai/v1/chat/completions
* Runtime : Node.js >= 18 (zero dep)
*
* Credits : FazzCodeID
* ============================================================
*/
import fs from "fs";
import crypto from "crypto";
import { pathToFileURL } from "url";
const CONFIG = {
BASE_URL: "https://api.overchat.ai/v1",
MODEL: "openai/gpt-4o",
PERSONA_ID: "gpt-4o-landing",
DEVICE_PLATFORM: "web",
DEVICE_LANGUAGE: "id-ID",
DEVICE_VERSION: "1.0.44",
ORIGIN: "https://overchat.ai",
TIMEOUT_MS: 180_000,
DEFAULT_PROMPT: "Siapa Itu FazzCodeID?",
TEMPERATURE: 0.5,
TOP_P: 0.95,
MAX_TOKENS: 4000,
FREQUENCY_PENALTY: 0,
PRESENCE_PENALTY: 0,
};
const ts = () => new Date().toISOString().slice(11, 19);
const log = {
info: (t, m) => console.error(`[${ts()}] [${t}] ${m}`),
ok: (t, m) => console.error(`[${ts()}] [\x1b[32m${t}\x1b[0m] ${m}`),
warn: (t, m) => console.error(`[${ts()}] [\x1b[33m${t}\x1b[0m] ${m}`),
err: (t, m) => console.error(`[${ts()}] [\x1b[31m${t}\x1b[0m] ${m}`),
};
const uuid = () => crypto.randomUUID();
function coerce(v) {
if (typeof v !== "string") return v;
if (v.trim() === "") return v;
if (/^-?\d+$/.test(v)) return parseInt(v, 10);
if (/^-?\d*\.\d+$/.test(v)) return parseFloat(v);
if (v === "true") return true;
if (v === "false") return false;
return v;
}
function buildMessages(prompt, options = {}) {
if (Array.isArray(options.messages) && options.messages.length > 0) {
return options.messages;
}
const msgs = [];
msgs.push({ id: uuid(), role: "system", content: options.systemPrompt ?? "" });
msgs.push({ id: uuid(), role: "user", content: prompt });
return msgs;
}
function buildHeaders() {
return {
"Content-Type": "application/json",
Accept: "*/*",
"X-Device-Platform": CONFIG.DEVICE_PLATFORM,
"X-Device-Language": CONFIG.DEVICE_LANGUAGE,
"X-Device-Uuid": uuid(),
"X-Device-Version": CONFIG.DEVICE_VERSION,
Origin: CONFIG.ORIGIN,
"User-Agent":
"Mozilla/5.0 (Linux; Android 13; SM-G991B) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
Referer: `${CONFIG.ORIGIN}/`,
};
}
export async function chat(prompt, options = {}) {
const tStart = Date.now();
const chatId = options.chatId || uuid();
const messages = buildMessages(prompt, options);
const result = {
success: false,
chatId,
model: options.model || CONFIG.MODEL,
personaId: options.personaId || CONFIG.PERSONA_ID,
prompt,
messages_count: messages.length,
content: null,
chunks: 0,
finish_reason: null,
provider: null,
usage: null,
error: null,
timing: {},
streaming: false,
stream_forced: false,
};
try {
const body = {
chatId,
model: options.model || CONFIG.MODEL,
messages,
personaId: options.personaId || CONFIG.PERSONA_ID,
frequency_penalty: options.frequencyPenalty ?? CONFIG.FREQUENCY_PENALTY,
max_tokens: options.maxTokens ?? CONFIG.MAX_TOKENS,
presence_penalty: options.presencePenalty ?? CONFIG.PRESENCE_PENALTY,
stream: false,
temperature: options.temperature ?? CONFIG.TEMPERATURE,
top_p: options.topP ?? CONFIG.TOP_P,
};
log.info("POST", `/chat/completions (request stream=false)`);
log.info("BODY", `model=${body.model} persona=${body.personaId} chatId=${chatId}`);
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? CONFIG.TIMEOUT_MS);
let res;
try {
res = await fetch(`${CONFIG.BASE_URL}/chat/completions`, {
method: "POST",
signal: controller.signal,
headers: buildHeaders(),
body: JSON.stringify(body),
});
} finally {
clearTimeout(timer);
}
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(`HTTP ${res.status} ${res.statusText}: ${text.slice(0, 300)}`);
}
const contentType = (res.headers.get("content-type") || "").toLowerCase();
log.info("CT", `content-type: ${contentType}`);
// Branch 1: JSON
if (contentType.includes("application/json")) {
const json = await res.json();
const choice = json?.choices?.[0];
if (!choice) throw new Error(`Respons tidak valid: ${JSON.stringify(json).slice(0, 200)}`);
result.content = choice.message?.content ?? null;
result.finish_reason = choice.finish_reason ?? null;
result.usage = json.usage ?? null;
result.provider = json.provider ?? null;
result.model = json.model ?? result.model;
result.raw = json;
result.streaming = false;
log.ok("MODE", "JSON non-stream");
}
// Branch 2: SSE
else {
log.warn("MODE", "Server paksa SSE (stream:false diabaikan)");
result.stream_forced = true;
result.streaming = true;
const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
const aggregated = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let idx;
while ((idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx).trim();
buffer = buffer.slice(idx + 1);
if (!line) continue;
if (line.startsWith(":")) continue;
if (!line.startsWith("data:")) continue;
const payload = line.slice(5).trim();
if (payload === "[DONE]") continue;
let chunk;
try {
chunk = JSON.parse(payload);
} catch {
continue;
}
if (chunk.provider) result.provider = chunk.provider;
if (chunk.model) result.model = chunk.model;
if (chunk.usage) result.usage = chunk.usage;
const delta = chunk?.choices?.[0]?.delta;
if (delta?.content) {
aggregated.push(delta.content);
result.chunks++;
options.onChunk?.(delta.content);
}
const finish = chunk?.choices?.[0]?.finish_reason;
if (finish) result.finish_reason = finish;
}
}
result.content = aggregated.join("");
}
result.success = true;
result.timing.total = Date.now() - tStart;
log.ok("DONE", `${result.timing.total}ms | ${result.chunks} chunks | ${(result.content || "").length} char`);
if (result.provider) log.info("PROVIDER", result.provider);
} catch (err) {
result.error = { message: err.message };
result.timing.total = Date.now() - tStart;
log.err("ERROR", err.message);
}
return result;
}
export async function chatStream(prompt, options = {}) {
const tStart = Date.now();
const chatId = options.chatId || uuid();
const messages = buildMessages(prompt, options);
const result = {
success: false,
chatId,
model: options.model || CONFIG.MODEL,
personaId: options.personaId || CONFIG.PERSONA_ID,
prompt,
messages_count: messages.length,
content: "",
chunks: 0,
finish_reason: null,
provider: null,
error: null,
timing: {},
streaming: true,
};
try {
const body = {
chatId,
model: options.model || CONFIG.MODEL,
messages,
personaId: options.personaId || CONFIG.PERSONA_ID,
frequency_penalty: options.frequencyPenalty ?? CONFIG.FREQUENCY_PENALTY,
max_tokens: options.maxTokens ?? CONFIG.MAX_TOKENS,
presence_penalty: options.presencePenalty ?? CONFIG.PRESENCE_PENALTY,
stream: true,
temperature: options.temperature ?? CONFIG.TEMPERATURE,
top_p: options.topP ?? CONFIG.TOP_P,
};
log.info("POST", `/chat/completions (stream=true)`);
log.info("BODY", `model=${body.model} persona=${body.personaId} chatId=${chatId}`);
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? CONFIG.TIMEOUT_MS);
let res;
try {
res = await fetch(`${CONFIG.BASE_URL}/chat/completions`, {
method: "POST",
signal: controller.signal,
headers: buildHeaders(),
body: JSON.stringify(body),
});
} finally {
clearTimeout(timer);
}
if (!res.ok || !res.body) {
const text = await res.text().catch(() => "");
throw new Error(`HTTP ${res.status}: ${text.slice(0, 300)}`);
}
const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let idx;
while ((idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx).trim();
buffer = buffer.slice(idx + 1);
if (!line) continue;
if (line.startsWith(":")) continue;
if (!line.startsWith("data:")) continue;
const payload = line.slice(5).trim();
if (payload === "[DONE]") continue;
let chunk;
try {
chunk = JSON.parse(payload);
} catch {
continue;
}
if (chunk.provider) result.provider = chunk.provider;
if (chunk.model) result.model = chunk.model;
const delta = chunk?.choices?.[0]?.delta;
if (delta?.content) {
result.content += delta.content;
result.chunks++;
options.onChunk?.(delta.content);
}
const finish = chunk?.choices?.[0]?.finish_reason;
if (finish) result.finish_reason = finish;
}
}
result.success = true;
result.timing.total = Date.now() - tStart;
log.ok("DONE", `${result.timing.total}ms | ${result.chunks} chunks | ${result.content.length} char`);
if (result.provider) log.info("PROVIDER", result.provider);
} catch (err) {
result.error = { message: err.message };
result.timing.total = Date.now() - tStart;
log.err("ERROR", err.message);
}
return result;
}
function printBanner(prompt, opts) {
console.error(" Overchat.ai Scraper (auto SSE/JSON) ");
console.log(" ")
console.error(` Model : ${opts.model}`);
console.error(` Persona : ${opts.personaId}`);
console.error(` Prompt : ${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}`);
console.error(` Stream : ${opts.stream ? "yes" : "auto"}`);
console.error("");
}
function parseArgs(argv) {
const flags = {};
const positional = [];
for (const a of argv) {
if (a.startsWith("--")) {
const [k, v] = a.slice(2).split("=");
flags[k] = v ?? true;
} else {
positional.push(a);
}
}
return { positional, flags };
}
async function main() {
const { positional, flags } = parseArgs(process.argv.slice(2));
if (positional.length === 0 && !flags.prompt) {
console.error("Usage:");
console.error(' node ai/gpt4o.js "hallo"');
console.error(' node ai/gpt4o.js "hallo" --stream');
console.error("");
console.error("Flags:");
console.error(' --prompt="..." Prompt');
console.error(' --system="..." System prompt');
console.error(" --model=... Model (default: openai/gpt-4o)");
console.error(" --persona=... PersonaId");
console.error(" --chatId=uuid Chat ID");
console.error(" --temp=0.5 Temperature");
console.error(" --top_p=0.95 Top-p");
console.error(" --maxTokens=4000 Max tokens");
console.error(" --stream Streaming mode");
console.error(" --show-content Live print ke stderr");
console.error(" --out=answer.txt Simpan content");
process.exit(1);
}
const prompt = flags.prompt || positional[0] || CONFIG.DEFAULT_PROMPT;
const opts = {
model: flags.model || CONFIG.MODEL,
personaId: flags.persona || CONFIG.PERSONA_ID,
chatId: flags.chatId,
systemPrompt: flags.system,
temperature: flags.temp != null ? coerce(flags.temp) : undefined,
topP: flags.top_p != null ? coerce(flags.top_p) : undefined,
maxTokens: flags.maxTokens != null ? coerce(flags.maxTokens) : undefined,
timeoutMs: flags.timeout ? coerce(flags.timeout) : undefined,
stream: !!flags.stream,
};
printBanner(prompt, opts);
let result;
if (opts.stream) {
result = await chatStream(prompt, {
...opts,
onChunk: flags["show-content"]
? (chunk) => process.stderr.write(chunk)
: undefined,
});
if (flags["show-content"]) process.stderr.write("\n");
} else {
result = await chat(prompt, opts);
}
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
if (result.success && flags.out && result.content) {
fs.writeFileSync(flags.out, result.content, "utf-8");
log.ok("SAVE", `${result.content.length} char -> ${flags.out}`);
}
process.exit(result.success ? 0 : 1);
}
const isDirectRun =
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href;
if (isDirectRun) {
main().catch((e) => {
console.error("Fatal:", e);
process.exit(1);
});
}
Discussion
Thread
Belum ada komentarJadilah yang pertama membuka diskusi.