SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
3.8 KB · 115 lines typescript
Raw Blame History
1// Shared helpers for OpenRouter probes. Key comes from process.env.OPENROUTER_API_KEY (never logged).2import { mkdirSync, writeFileSync } from "node:fs";3import OpenAI from "openai";45export const BASE = "https://openrouter.ai/api/v1";6export const KEY = process.env.OPENROUTER_API_KEY ?? "";7if (!KEY) {8  console.error("OPENROUTER_API_KEY missing (run: set -a; . ./.env; set +a)");9  process.exit(1);10}1112export const OUT = new URL("./out/", import.meta.url).pathname;13mkdirSync(OUT, { recursive: true });1415export const APP_HEADERS = { "HTTP-Referer": "https://www.polyllm.io", "X-Title": "PolyLLM research" };1617export const client = new OpenAI({18  apiKey: KEY,19  baseURL: BASE,20  timeout: 120_000,21  maxRetries: 0,22  defaultHeaders: APP_HEADERS,23});2425export function save(name: string, data: unknown) {26  writeFileSync(`${OUT}${name}`, JSON.stringify(data, null, 2));27  console.log(`saved out/${name}`);28}2930const HEADER_RE = /ratelimit|request-id|x-request|retry-after|content-type|server|date|cf-ray|x-openrouter|x-or-/i;3132/** Raw request; returns status, selected headers, and parsed body (never echoes the key). */33export async function raw(path: string, init: RequestInit & { key?: string } = {}) {34  const { key, ...rest } = init;35  const res = await fetch(`${BASE}${path}`, {36    ...rest,37    headers: {38      "Content-Type": "application/json",39      Authorization: `Bearer ${key ?? KEY}`,40      ...APP_HEADERS,41      ...(rest.headers ?? {}),42    },43  });44  const text = await res.text();45  let body: unknown = text;46  try {47    body = JSON.parse(text);48  } catch {}49  const headers: Record<string, string> = {};50  res.headers.forEach((v, k) => {51    if (HEADER_RE.test(k)) headers[k] = v;52  });53  return { status: res.status, headers, body };54}5556/** Raw SSE POST; returns parsed data events plus every comment line (": OPENROUTER PROCESSING"). */57export async function rawSSE(path: string, payload: unknown, key?: string) {58  const res = await fetch(`${BASE}${path}`, {59    method: "POST",60    headers: { "Content-Type": "application/json", Authorization: `Bearer ${key ?? KEY}`, ...APP_HEADERS },61    body: JSON.stringify(payload),62  });63  const headers: Record<string, string> = {};64  res.headers.forEach((v, k) => {65    if (HEADER_RE.test(k)) headers[k] = v;66  });67  if (!res.ok || !res.body) {68    const text = await res.text();69    let body: unknown = text;70    try {71      body = JSON.parse(text);72    } catch {}73    return { status: res.status, headers, error: body, events: [] as any[], comments: [] as string[] };74  }75  const reader = res.body.getReader();76  const dec = new TextDecoder();77  let buf = "";78  const events: { event?: string; data: any; raw?: string }[] = [];79  const comments: string[] = [];80  for (;;) {81    const { value, done } = await reader.read();82    if (done) break;83    buf += dec.decode(value, { stream: true });84    let idx;85    while ((idx = buf.indexOf("\n\n")) >= 0) {86      const block = buf.slice(0, idx);87      buf = buf.slice(idx + 2);88      let ev: string | undefined;89      const datas: string[] = [];90      for (const line of block.split("\n")) {91        if (line.startsWith(":")) comments.push(line);92        else if (line.startsWith("event:")) ev = line.slice(6).trim();93        else if (line.startsWith("data:")) datas.push(line.slice(5).trim());94      }95      if (!datas.length) continue;96      const d = datas.join("\n");97      if (d === "[DONE]") {98        events.push({ event: ev, data: "[DONE]" });99        continue;100      }101      try {102        events.push({ event: ev, data: JSON.parse(d) });103      } catch {104        events.push({ event: ev, data: null, raw: d });105      }106    }107  }108  return { status: res.status, headers, events, comments };109}110111export function short(s: unknown, n = 300) {112  const t = typeof s === "string" ? s : JSON.stringify(s) ?? String(s);113  return t.length > n ? t.slice(0, n) + "…" : t;114}115