SPB Git

spb/search-box Public

Agentic web research engine — hypotheses, verbatim evidence, contradictions, sourced answers streamed live. Claude Opus 5 + Firecrawl + PostgreSQL.

TypeScript 76.9% CSS 18.7% SQL 2.1% JavaScript 1.8% Shell 0.5%
2.4 KB · 86 lines typescript
Raw Blame History
1/**2 * Search-box.ai3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: packages/anthropic/src/index.ts6 * Description: Anthropic Messages API adapter — singleton client, model config, streaming helpers.7 */89import Anthropic from "@anthropic-ai/sdk";1011let client: Anthropic | null = null;1213export function getAnthropic(): Anthropic {14  if (!client) {15    if (!process.env.ANTHROPIC_API_KEY) throw new Error("ANTHROPIC_API_KEY is not set");16    client = new Anthropic({ maxRetries: 3 });17  }18  return client;19}2021export interface ModelConfig {22  orchestrator: string;23  researcher: string;24  verifier: string;25  synthesis: string;26}2728const DEFAULT_MODEL = "claude-opus-5";2930export function getModels(): ModelConfig {31  return {32    orchestrator: process.env.CLAUDE_ORCHESTRATOR_MODEL || DEFAULT_MODEL,33    researcher: process.env.CLAUDE_RESEARCHER_MODEL || DEFAULT_MODEL,34    verifier: process.env.CLAUDE_VERIFIER_MODEL || DEFAULT_MODEL,35    synthesis: process.env.CLAUDE_SYNTHESIS_MODEL || DEFAULT_MODEL36  };37}3839/**40 * One orchestrator turn (non-streaming). Uses streaming under the hood via41 * the SDK helper so long turns don't hit HTTP timeouts.42 */43export async function orchestratorTurn(params: {44  model: string;45  system: Anthropic.TextBlockParam[];46  messages: Anthropic.MessageParam[];47  tools: Anthropic.ToolUnion[];48  maxTokens?: number;49}): Promise<Anthropic.Message> {50  const stream = getAnthropic().messages.stream({51    model: params.model,52    max_tokens: params.maxTokens ?? 16000,53    output_config: { effort: "medium" },54    system: params.system,55    messages: params.messages,56    tools: params.tools57  });58  return stream.finalMessage();59}6061/**62 * Streaming synthesis call. Invokes `onDelta` per text token; resolves with63 * the final message once complete.64 */65export async function synthesisStream(params: {66  model: string;67  system: Anthropic.TextBlockParam[];68  messages: Anthropic.MessageParam[];69  maxTokens?: number;70  onDelta: (delta: string) => void | Promise<void>;71}): Promise<Anthropic.Message> {72  const stream = getAnthropic().messages.stream({73    model: params.model,74    max_tokens: params.maxTokens ?? 24000,75    output_config: { effort: "medium" },76    system: params.system,77    messages: params.messages78  });79  stream.on("text", (delta) => {80    void params.onDelta(delta);81  });82  return stream.finalMessage();83}8485export type { Anthropic };86