SPB Git

spb/spbgit

Public MIT Pinned

SPB Git — the platform hosting itself

2.4 KB · 52 lines javascript
1/**2 * ─────────────────────────────────────────────3 *  SPB Git — Personal Git Platform4 * ─────────────────────────────────────────────5 *  Author  : Simon-Pierre Boucher6 *  Contact : contact@spboucher.ai7 *  File    : src/ai/providers/anthropic.mjs8 *  Purpose : AI provider — Claude via the official Anthropic SDK (server-side key, server-side refusal fallbacks)9 *  License : MIT © Simon-Pierre Boucher10 * ─────────────────────────────────────────────11 */1213import Anthropic from '@anthropic-ai/sdk';1415const DEFAULT_MODEL = 'claude-opus-5-5';1617/**18 * @param {{apiKey: string, model?: string, baseUrl?: string}} cfg19 * @returns {{name: string, model: string, complete: (req: {system: string, prompt: string, maxTokens: number}) => Promise<{text: string, usage: {input: number, output: number}, model: string}>}}20 */21export function createAnthropicProvider(cfg) {22  if (!cfg.apiKey) throw new Error('SPBGIT_AI_API_KEY is required for the anthropic provider');23  const client = new Anthropic({ apiKey: cfg.apiKey, ...(cfg.baseUrl ? { baseURL: cfg.baseUrl } : {}) });24  const model = cfg.model || DEFAULT_MODEL;25  return {26    name: 'anthropic',27    model,28    async complete({ system, prompt, maxTokens }) {29      const response = await client.beta.messages.create({30        model,31        max_tokens: maxTokens,32        // Safety-classifier declines are re-routed server-side by category.33        betas: ['server-side-fallback-2026-07-01'],34        fallbacks: 'default',35        system,36        messages: [{ role: 'user', content: prompt }],37      });38      if (response.stop_reason === 'refusal') {39        const why = response.stop_details?.explanation ?? 'the request was declined by the model safety system';40        throw Object.assign(new Error(`The model declined to answer: ${why}`), { code: 'refusal' });41      }42      const text = response.content.filter((b) => b.type === 'text').map((b) => b.text).join('\n').trim();43      return {44        text,45        truncated: response.stop_reason === 'max_tokens',46        usage: { input: response.usage?.input_tokens ?? 0, output: response.usage?.output_tokens ?? 0 },47        model: response.model ?? model,48      };49    },50  };51}52
Files · main
Menu

Theme