Basalt is a powerful tool for managing AI prompts and their release workflows. This SDK is the official NodeJS package for interacting with your Basalt prompts.
Install the Basalt SDK via npm:
npm install @basalt-ai/sdkTo get started, import the BasaltSDK class and initialize it with your API key:
// Using module syntax
import { Basalt } from '@basalt-ai/sdk'
// Using CommonJS syntax
const { Basalt } = require('@basalt-ai/sdk');
const basalt = new Basalt({
apiKey: 'your-api-key-here',
});
// Don't forget to put your apiKey into an env variable
const basalt = new Basalt({
apiKey: process.env.BASALT_API_KEY
})Tracing is enabled by default for all Basalt SDK routes. To export spans, configure the built-in OTLP gRPC exporter:
import { Basalt } from '@basalt-ai/sdk'
const basalt = new Basalt({
apiKey: process.env.BASALT_API_KEY!,
telemetry: {
// For a local collector without TLS
insecure: true,
serviceName: 'my-app',
// Optionally override the collector endpoint
// endpoint: 'localhost:4317',
},
})All spans include Basalt metadata attributes (e.g. basalt.sdk, basalt.version, basalt.span_type, and basalt.meta.*) and propagate trace context headers (traceparent, etc.) on outgoing Basalt API requests.
- Recommended: consume the published package (
npm install @basalt-ai/sdk). - Advanced (source/submodule): if you bundle SDK source in your own build pipeline, make sure SDK compile-time constants are provided (
__PUBLIC_API_URL__,__SDK_VERSION__,__SDK_TARGET__) or keep runtime fallbacks enabled.
- Check
BASALT_API_KEYis set and valid. - Check exporter endpoint (
BASALT_OTEL_EXPORTER_OTLP_ENDPOINT) points to your collector. - Ensure telemetry is enabled (
telemetry.enabled !== false). - If using OpenAI auto-instrumentation, install
@opentelemetry/instrumentation-openaiand callbasalt.instrument({ openai: true })before loading OpenAI. - For local debugging in Node, set
BASALT_DEBUG_STDOUT_SPANS=1to print exported spans.
To add custom attributes to the current active span:
import { setCurrentSpanAttributes } from '@basalt-ai/sdk'
setCurrentSpanAttributes({
'basalt.meta.customer_id': 'cust_123',
})To attach context that will be added to all Basalt spans created within a scope:
import { BasaltContextManager } from '@basalt-ai/sdk'
await BasaltContextManager.withMergedContext(
{
user: { id: 'user_1' },
metadata: { env: 'prod' },
},
async () => {
// Calls inside this function will automatically include these attributes
// in Basalt spans.
},
)-
Constructors
The Basalt constructor accepts following options:
-
apiKey: string: The API Key you generated from your Basalt workspace. -
logLevel?: "all" | "warning" | "none"Configure what Basalt logs to the console. Recommended:
warningat dev,nonein production.
-
The prompt attribute provides methods to interact with your Basalt prompts:
-
Get a Prompt
Retrieve a specific prompt using a slug, and optional filters
tagandversion. Without tag or version would, the production version of your prompt is selected by default.Example Usage:
// With a single object. Tag and filters are still optional, // use this if you need more control over the specific version of // the prompt to select const result = await basalt.prompt.get({ slug: 'prompt-slug', tag: 'custom-tag' }); const result = await basalt.prompt.get({ slug: 'prompt-slug', version: '1.0.0' }); // With the slug, leaving the rest optional // this is useful for easily fetching the prod version of prompts const result = await basalt.prompt.get('prompt-slug'); // This is also valid const result = await basalt.prompt.get('prompt-slug', { tag: 'latest' }); const result = await basalt.prompt.get('prompt-slug', { version: '1.0.0' }); // If your prompt has variables,they need to be provided // in the options. // Example prompt: "Hello {{name}}" const result = await basalt.prompt.get({ slug: 'prompt-slug', variables: { name: "John Doe" } }) // Handle the result by unwrapping the error / value if (result.error) { console.log('Could not fetch prompt', result.error.message); return; } // Use the prompt with your AI provider of choice // Example: OpenAI openaiClient.chat.completion.create({ model: 'gpt-4o' messages: [{ role: 'User', content: result.value.text }] })
The Basalt SDK includes an in-memory caching mechanism to improve performance by reducing redundant API calls. When you request a prompt multiple times with the same parameters, the SDK can serve it from the cache instead of making additional network requests.
The SDK implements a simple but effective in-memory cache through the MemoryCache class that:
- Stores key-value pairs in memory
- Supports configurable time-to-live (TTL) for each cached entry
- Automatically invalidates expired entries
// Example of how caching works internally
// (You don't need to implement this yourself)
// First request fetches from API and caches the result
const result1 = await basalt.prompt.get('my-prompt');
// Subsequent identical requests within the TTL period
// will be served from cache
const result2 = await basalt.prompt.get('my-prompt');By default, the cache is enabled with reasonable defaults. You can configure the cache behavior when initializing the SDK:
const basalt = new Basalt({
apiKey: process.env.BASALT_API_KEY,
cache: {
enabled: true, // Enable/disable caching (default: true)
ttl: 60000, // Default TTL in milliseconds (default: 60000 - 1 minute)
}
});- Improved Performance: Reduces latency for frequently accessed prompts
- Reduced API Usage: Minimizes the number of API calls to the Basalt service
- Offline Resilience: Previously cached prompts remain available even during temporary network issues
The cache is particularly useful in high-throughput applications where the same prompts are requested multiple times in a short period.
If you were using the legacy manual monitoring system (removed in v2.x), migrate to OpenTelemetry:
Before (Legacy):
const result = await basalt.prompt.get('my-prompt');
const generation = result.generation; // No longer available
await basalt.monitor.createExperiment(...)
trace.end(); // No longer availableAfter (OpenTelemetry):
// Use startObserve() for root spans with experiment tracking
const span = basalt.startObserve({
name: 'my-operation',
featureSlug: 'my-feature',
experiment: {
id: 'exp-1',
name: 'My Experiment',
featureSlug: 'my-feature',
},
});
// Wrap operations with the root span context
await BasaltContextManager.withRootSpan(span, async () => {
// Your operations here - child spans will auto-connect
const result = await basalt.prompt.get('my-prompt');
// ...
});
// End the span
span.end();Key changes:
basalt.prompt.get()no longer returns agenerationobjectbasalt.monitor.*methods removed - use OpenTelemetry spans instead- Use
basalt.startObserve()orbasalt.observe()for tracing - See OpenTelemetry section above for full documentation