import type { Config } from "./config"; export type ClientResult = | { ok: true; data: T } | { ok: false; error: string; status?: number }; type ErrorShape = { success?: boolean; message?: string; error?: { message?: string }; response?: string; }; async function parseErrorBody(response: Response): Promise { try { const body = await response.json() as ErrorShape; return ( body.error?.message ?? body.message ?? body.response ?? `HTTP ${response.status}: ${response.statusText}` ); } catch { return `HTTP ${response.status}: ${response.statusText}`; } } function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } async function request( baseUrl: string, apiKey: string, method: string, path: string, body?: unknown, ): Promise> { const url = `${baseUrl}${path}`; const delays = [250, 1000]; for (let attempt = 0; attempt <= delays.length; attempt++) { try { const response = await fetch(url, { method, headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }, body: body ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(15_000), }); if (response.ok) { const data = (await response.json()) as T; return { ok: true, data }; } const errorMsg = await parseErrorBody(response); if (response.status >= 400 && response.status < 500) { if (response.status === 401 || response.status === 403) { return { ok: false, error: "Alchemyst API auth failed — check ALCHEMYST_API_KEY", status: response.status, }; } if (response.status === 402) { return { ok: false, error: "Alchemyst account has a billing/plan restriction blocking this call.", status: response.status, }; } if (response.status === 429) { return { ok: false, error: "Rate limit exceeded. Please wait and try again.", status: response.status, }; } return { ok: false, error: errorMsg, status: response.status }; } if (attempt < delays.length) { await sleep(delays[attempt]); continue; } return { ok: false, error: errorMsg, status: response.status }; } catch (error) { if (attempt < delays.length) { await sleep(delays[attempt]); continue; } return { ok: false, error: "Alchemyst context search unavailable right now.", }; } } return { ok: false, error: "Alchemyst context search unavailable right now." }; } export type SearchRequest = { query: string; similarity_threshold: number; minimum_similarity_threshold: number; scope?: "internal" | "external"; body_metadata?: Record; }; export type SearchResponse = { contexts: Array<{ content: string; score: number; createdAt?: string; updatedAt?: string; metadata?: Record; }>; }; export type AskResponse = { answer: string; model: string; usage: { inputTokens: number; outputTokens: number; totalTokens: number; }; context?: string; }; export type AddRequest = { documents: Array<{ content: string }>; source: string; context_type: "resource" | "instruction"; scope: "internal" | "external"; metadata?: Record; }; export type AddResponse = { success: boolean; context_id: string; processed_documents: number; }; export type MemoryAddRequest = { sessionId: string; contents: Array<{ content: string; metadata?: { messageId?: string }; }>; metadata?: { groupName?: string[] }; }; export type MemoryResponse = { success: boolean; context_id: string; processed_documents: number; }; export type MemoryUpdateRequest = { sessionId: string; contents: Array<{ content: string; role?: string; id?: string; createdAt?: string; metadata?: Record; }>; }; export type MemoryUpdateResponse = { success: boolean; memory_id: string; updated_entries: number; }; export function createClient(config: Config) { const { baseUrl, apiKey } = config; if (!apiKey) { throw new Error("ALCHEMYST_API_KEY is required to create a client"); } return { search( params: SearchRequest, mode: "fast" | "standard" = "standard", ): Promise> { return request( baseUrl, apiKey, "POST", `/api/v1/context/search${mode === "fast" ? "?mode=fast" : ""}`, params, ); }, ask( params: SearchRequest & { steeringPrompt?: string }, ): Promise> { return request( baseUrl, apiKey, "POST", "/api/v1/context/search/steer", params, ); }, add(params: AddRequest): Promise> { return request( baseUrl, apiKey, "POST", "/api/v1/context/add", params, ); }, memoryAdd( params: MemoryAddRequest, ): Promise> { return request( baseUrl, apiKey, "POST", "/api/v1/context/memory/add", params, ); }, memoryUpdate( params: MemoryUpdateRequest, ): Promise> { return request( baseUrl, apiKey, "POST", "/api/v1/context/memory/update", params, ); }, }; } export type AlchemystClient = ReturnType;