SPB Git

Repository Public MIT Selected

openai-compatible.mjs 2.4 KB 48 lines javascript

1/**2 * ─────────────────────────────────────────────3 *  SPB Git — Personal Git Platform4 * ─────────────────────────────────────────────5 *  Author  : Simon-Pierre Boucher6 *  Contact : contact@spboucher.ai7 *  File    : src/ai/providers/openai-compatible.mjs8 *  Purpose : AI provider — local inference servers speaking the OpenAI chat-completions shape (MLX, llama.cpp, vLLM); raw fetch, no SDK9 *  License : MIT © Simon-Pierre Boucher10 * ─────────────────────────────────────────────11 */1213/**14 * @param {{baseUrl: string, apiKey?: string, model: string}} cfg base URL without the `/chat/completions` suffix15 */16export function createOpenAICompatibleProvider(cfg) {17  if (!cfg.baseUrl) throw new Error('SPBGIT_AI_BASE_URL is required for the openai-compatible provider');18  if (!cfg.model) throw new Error('SPBGIT_AI_MODEL is required for the openai-compatible provider');19  const url = `${cfg.baseUrl.replace(/\/+$/, '')}/chat/completions`;20  return {21    name: 'openai-compatible',22    model: cfg.model,23    async complete({ system, prompt, maxTokens }) {24      const controller = new AbortController();25      const timer = setTimeout(() => controller.abort(), 120000);26      try {27        const res = await fetch(url, {28          method: 'POST',29          headers: { 'Content-Type': 'application/json', ...(cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {}) },30          body: JSON.stringify({ model: cfg.model, max_tokens: maxTokens, temperature: 0.2, messages: [{ role: 'system', content: system }, { role: 'user', content: prompt }] }),31          signal: controller.signal,32        });33        if (!res.ok) throw new Error(`inference server returned HTTP ${res.status}`);34        const data = await res.json();35        const choice = data.choices?.[0];36        return {37          text: String(choice?.message?.content ?? '').trim(),38          truncated: choice?.finish_reason === 'length',39          usage: { input: data.usage?.prompt_tokens ?? 0, output: data.usage?.completion_tokens ?? 0 },40          model: data.model ?? cfg.model,41        };42      } finally {43        clearTimeout(timer);44      }45    },46  };47}48
Files main
Menu

Theme