Client
Construct the Keelwave client, configure the endpoint and error behaviour, and send model traces.
Keelwave is the entry point for the TypeScript SDK. One client holds your API
key and server endpoint; everything else — runs, decorators, spans, the Vercel
AI adapter — hangs off it.
import { Keelwave } from 'keelwave'
const client = new Keelwave({
apiKey: process.env.KEELWAVE_API_KEY ?? '',
endpoint: process.env.KEELWAVE_ENDPOINT ?? 'http://localhost:8080',
})
const health = await client.health()
console.log(health.status) // "ok"The SDK targets Node 22+ and ships as ESM with bundled type declarations, so it
works from plain JavaScript or TypeScript. It uses node:crypto and
AsyncLocalStorage, so it is not built for the browser.
npm install keelwaveKeelwaveOptions
| Option | Type | Default | Notes |
|---|---|---|---|
apiKey | string | — (required) | keelwave API key (kw_…). |
endpoint | string | http://localhost:8080 | Server base URL. A trailing / is trimmed. |
raiseOnError | boolean | false | When false, emit failures log a warning instead of throwing. |
The three values are exposed as readonly properties on the client:
client.apiKey, client.endpoint, client.raiseOnError.
new Keelwave({ apiKey: 'kw_x', endpoint: 'http://example.com:8080/' }).endpoint
// → "http://example.com:8080"Fail-soft by default
With the default raiseOnError: false, a failed emit never breaks your agent.
The client warns on console and your code keeps running — even if the
keelwave server is unreachable.
const client = new Keelwave({ apiKey: 'kw_x', endpoint: 'http://127.0.0.1:9' })
const out = await client.run('offline-agent', async (run) => {
await run.step('think', 'no server here')
return 'ok'
})
// out === "ok" — a local run id is synthesised, nothing throwsSet raiseOnError: true in tests or CI when you want ingest problems to
surface as typed exceptions.
const strict = new Keelwave({ apiKey: 'kw_x', raiseOnError: true })raiseOnError only changes what happens when telemetry fails. Errors thrown by
your own agent code always propagate.
health()
Checks the server is reachable and returns its status envelope.
const { status, env, version } = await client.health()Signature: health(): Promise<{ status: string; env: string; version: string }>.
It calls GET /v1/health. Unlike the ingest methods, health() always throws
on failure regardless of raiseOnError.
ingestAi()
Records a single model call. Use it when instrumenting a provider by hand — the Vercel AI adapter calls it for you.
await client.ingestAi({
model: 'gpt-4o',
provider: 'openai',
status: 'success',
inputTokens: 100,
outputTokens: 50,
latencyMs: 300,
costUsd: 0.002,
})IngestAiOptions
| Field | Type | Required | Notes |
|---|---|---|---|
model | string | yes | Model label, e.g. gpt-4o. |
status | 'success' | 'error' | 'timeout' | yes | Outcome of the call. |
provider | string | no | Provider label, e.g. openai. |
inputTokens | number | no | Prompt tokens. |
outputTokens | number | no | Completion tokens. |
totalTokens | number | no | Total tokens, if the provider reports it directly. |
costUsd | number | no | Cost in USD. |
latencyMs | number | no | Call latency in milliseconds. |
errorMessage | string | no | Error text when status is not success. |
requestId | string | no | Provider request id, for correlation. |
agentRunId | string | no | Links this trace to a run (run.id). |
metadata | Record<string, unknown> | no | Arbitrary JSON. |
ingestAi posts to POST /v1/ingest/ai and resolves to void. It is
fail-soft: transport errors are routed through the raiseOnError policy.
To attach a trace to the surrounding agent run, pass the run id:
import { getCurrentRun } from 'keelwave'
await client.run('research', async () => {
await client.ingestAi({
model: 'gpt-4o',
provider: 'openai',
status: 'success',
agentRunId: getCurrentRun()?.id,
})
})Client surface
| Member | Purpose |
|---|---|
health() | Server reachability check. |
ingestAi(opts) | Record one model call. |
run(name, fn, opts?) | Open an agent run — see Runs. |
observe(fn | opts) | Wrap a function as a traced step — see Span. |
agent(fn | opts) | Wrap an async function so it opens and closes a run. |
span(stepType?, name?) | Create a Span for a manually timed step. |
getCurrentRun() | The ambient Run, or undefined outside a run. |
Environment variables
The SDK does not read the environment itself — pass values in explicitly. The examples in the SDK repo use these names by convention:
KEELWAVE_API_KEY— yourkw_…API key.KEELWAVE_ENDPOINT— server base URL.
const client = new Keelwave({
apiKey: process.env.KEELWAVE_API_KEY ?? '',
endpoint: process.env.KEELWAVE_ENDPOINT ?? 'http://localhost:8080',
})