AACSearch
SDKs

TypeScript SDK

Der generierte Client @repo/sdk — Konstruktoroptionen, alle Methoden, Retry-Verhalten und der Typ AACSearchError.

@repo/sdk ist ein typisierter Client, generiert aus packages/api/routes/v1/openapi.json (neu bauen mit pnpm sdk:generate). Es ist ein ESM-Paket auf Basis des globalen fetch und läuft damit in Node 18+, Bun, Deno und Edge-Runtimes.

import { AACSearchClient, AACSearchError } from "@repo/sdk";

const client = new AACSearchClient({
	apiKey: process.env.AAC_KEY!,
	baseUrl: "https://search.example.com/api/v1",
});

const result = await client.search("documentation", { q: "preise", per_page: 5 });
console.log(result.found, result.hits[0]?.document.title);

Konstruktor

interface AACSearchClientOptions {
	/** aac_live_… Schlüssel. Pflicht. */
	apiKey: string;
	/** Standard: http://localhost:3000/api/v1 */
	baseUrl?: string;
	/** Wiederholungen für 429 und 5xx. Standard 2. */
	maxRetries?: number;
	/** Eigene fetch-Implementierung, z. B. für Tracing oder einen Proxy. */
	fetch?: typeof fetch;
}

Ein leerer apiKey wirft sofort, vor jedem Request:

new AACSearchClient({ apiKey: "" });
// AACSearchError: apiKey is required (status 401, code MISSING_API_KEY)

Ein abschließender Slash in baseUrl wird entfernt, https://host/api/v1/ und https://host/api/v1 sind also gleichwertig.

Fehler und Retries

Jeder Fehler ist ein AACSearchError mit dem geparsten Umschlag und dem HTTP-Status:

class AACSearchError extends Error {
	name: "AACSearchError";
	status: number; // 401, 403, 422, 429, 502 …
	code: string; // UNAUTHORIZED, QUOTA_EXCEEDED, VALIDATION_ERROR …
	details?: unknown; // details aus dem Fehler-Body
	requestId?: string; // im Supportfall angeben
}
try {
	await client.upsertDocuments("documentation", {
		documents: [{ external_id: "pricing", content: "Pro kostet 29 USD im Monat." }],
		action: "create",
	});
} catch (error) {
	if (error instanceof AACSearchError) {
		if (error.code === "QUOTA_EXCEEDED") {
			console.error(
				"Kontingent erschöpft, weiter ab",
				(error.details as { resetsAt: string }).resetsAt,
			);
		} else if (error.status === 429) {
			// Rate-Limit — das SDK hat maxRetries bereits ausgeschöpft
		} else {
			console.error(error.code, error.message, error.requestId);
		}
	}
	throw error;
}

Retry-Verhalten: 429 und alle 5xx werden bis zu maxRetries mal wiederholt (Standard 2) mit exponentiellem Backoff von 250 ms, 500 ms, 1 s. Andere 4xx werden nie wiederholt. Sind die Versuche erschöpft, wird die letzte Antwort geparst und geworfen. Fehlt im Body ein error-Objekt, wird code zu REQUEST_FAILED und die Meldung zu Request failed with <status>.

Timeouts erzwingt der Client nicht — übergeben Sie ein eigenes fetch mit AbortSignal, wenn Sie eines brauchen.

Methoden

Abfragen und Bodies sind einfache Objekte; der Client serialisiert nur definierte, nicht-leere Werte, undefined und "" werden also weggelassen statt als leere Parameter gesendet.

Suche und Discovery

MethodeHTTP
search(index, query)GET /indexes/{index}/search
searchPost(index, body)POST /indexes/{index}/search
suggest(index, query)GET /indexes/{index}/suggest
multiSearch(body)POST /indexes/multi_search
nlSearch(index, body)POST /indexes/{index}/nl-search
indexStats(index, query?)GET /indexes/{index}/stats
health()GET /health
capabilities()GET /capabilities
openapi()GET /openapi.json
metrics()GET /metrics
const suggestions = await client.suggest("documentation", { q: "pre", limit: 5 });
const stats = await client.indexStats("documentation");
const caps = await client.capabilities();
if (!caps.features.autoEmbedding)
	throw new Error("semantische Suche auf diesem Knoten nicht verfügbar");

Dokumente

MethodeHTTP
upsertDocuments(index, body)POST /indexes/{index}/documents
getDocument(index, externalId)GET /indexes/{index}/documents/{externalId}
deleteDocument(index, externalId)DELETE /indexes/{index}/documents/{externalId}
deleteDocuments(index, query)DELETE /indexes/{index}/documents?filter_by=…
bulkUpdateDocuments(index, body)POST /indexes/{index}/documents/bulk
exportDocuments(index)GET /indexes/{index}/documents/export
await client.upsertDocuments("documentation", {
	documents: [
		{
			external_id: "pricing",
			title: "Preise",
			content: "Pro kostet 29 USD im Monat und enthält 500.000 Suchen.",
			tags: ["billing"],
		},
	],
	action: "upsert",
});

const doc = await client.getDocument("documentation", "pricing");
await client.deleteDocument("documentation", "pricing");
await client.deleteDocuments("documentation", { filter_by: "source_type:=connector" });

KI

MethodeHTTP
answer(index, body)POST /indexes/{index}/answer — RAG-Antwort mit Quellen
assistantAsk(index, body)POST /indexes/{index}/assistant — Dialogzug
assistantStream(index, body, onEvent)POST /indexes/{index}/assistant/stream — SSE
assistantNative(index, body)POST /indexes/{index}/assistant/native — Dialogmodell der Engine
assistantThreads(index)GET /indexes/{index}/assistant
assistantThread(index, conversationId)GET /indexes/{index}/assistant/{conversationId}
conversationModels(index)GET /indexes/{index}/conversation-models
voiceSearch(index, body)POST /indexes/{index}/voice
imageSearch(index, body)POST /indexes/{index}/image
const answer = await client.answer("documentation", {
	question: "Was kostet der Pro-Tarif?",
	per_page: 4,
});
console.log(answer.answer, answer.citations);

await client.assistantStream("documentation", { question: "Und die Limits?" }, (event) => {
	if (event.type === "delta")
		process.stdout.write(String((event.payload as { text?: string }).text ?? ""));
});

assistantStream liest den Response-Body selbst, trennt Frames an einer Leerzeile, parst jede data: -Zeile und übergibt { type, payload }. Event-Typen sind citations, delta, done und error. Das Promise löst sich beim Schließen des Streams; ein leerer Body wirft AACSearchError mit Code SSE_EMPTY.

Relevanz und Betrieb

MethodeHTTP
listSynonyms(index) / putSynonym(index, body) / deleteSynonym(index, id)/indexes/{index}/synonyms
listCurations(index) / putCuration(index, body) / deleteCuration(index, id)/indexes/{index}/curations
trackEvent(index, body)POST /indexes/{index}/events
reindex(index)POST /indexes/{index}/reindex
scopedKey(index, body)POST /indexes/{index}/scoped-key
triggerSnapshot()POST /admin/snapshot
runJobs(query)POST /admin/jobs/run
exportOrganization()GET /organization/export
purgeOrganization(query)DELETE /organization/data

Analytics und Abrechnung

MethodeHTTP
analyticsOverview(query)GET /analytics/overview
analyticsOverviewCsv(query)GET /analytics/overview.csv
slowQueries(query)GET /analytics/slow-queries
trendingQueries(query)GET /analytics/trending
usage()GET /billing/usage
wallet()GET /billing/wallet
const { overview } = await client.analyticsOverview({ days: 7 });
console.log(overview.totals.searches, overview.totals.zeroResultRate);

const { usage } = await client.usage();
console.log(`${usage.searches.used}/${usage.searches.limit} Suchen in dieser Periode`);

Bekannte Lücken des generierten Clients

Der Client wird mechanisch aus dem OpenAPI-Dokument erzeugt, und drei Methoden passen nicht exakt zum Server. Sie stehen hier, damit Sie keinen Nachmittag damit verlieren:

MethodeProblemWorkaround
multiSuggest()Sendet keinen Body, obwohl der Endpunkt { indexes, q } verlangtfetch auf POST /indexes/suggest/multi
exportDocuments(index)Parst den NDJSON-Export mit response.json()Export per fetch holen und Text lesen
webhookIngest(index)Sendet weder Body noch Signatur-HeaderSelbst signieren und senden, siehe Webhooks

Alles andere entspricht eins zu eins einem dokumentierten Endpunkt.

On this page