@clavata/sdk
v0.5.0
Published
This directory contains the JS version of our SDK.
Downloads
3,835
Readme
Clavata JavaScript SDK
This directory contains the Clavata JavaScript SDK with type definitions.
Getting started
Email [email protected] if you do not already have an API key/token.
Initialize the SDK to get started making requests to the Clavata API.
import Clavata from "@clavata/sdk";
// API key can be provided directly or via environment variable
const client = new Clavata({ apiKey: "<YOUR_CLAVATA_API_KEY>" } as Options);Configuration
The client constructor accepts the following options:
interface Options {
// Your API key. If not provided you _must_ then set CLAVATA_API_KEY as an environment variable
apiKey?: string;
// Optional
server?: string; // Server endpoint (default: "gateway.app.clavata.ai:443")
insecure?: boolean; // Use insecure connection. (default: false)
keepalive?: {
// Connection keepalive settings
timeout: number; // How long to wait for a response to a keepalive ping before considering the connection dead (in ms). (default: 10000)
permitWithoutCalls: boolean; // Allows keepalive pings to maintain idle connections. (default: true)
time: number; // How frequently to send keepalive pings to check if the connection is alive (in ms). (default: 10000)
};
maxMessageLength?: number; // Max message size (default: 100MB)
retryConfig:
| {
// Configuration for automatic retries. Will retry by default. Set to `false` to disable this. See https://grpc.io/docs/guides/retry for more info.
maxAttempts?: number; // Maximum number of retry attempts (default: 5)
initialBackoff?: string; // Initial backoff time in milliseconds (default: "1s")
maxBackoff?: string; // Maximum backoff time (default: "30s")
backoffMultiplier?: number; // Multiplier for backoff time after each retry (default: 2.0)
jitter?: number; // Jitter factor between 0 and 1 (default: 0.2, 0 = no jitter, 1 = full jitter)
}
| false;
}If neither an API key is provided to the constructor nor is a process.env.CLAVATA_API_KEY environment variable set then the constructor will throw an error.
RPC Methods
getJob(jobId: string): Promise<Job | undefined>
- Retrieves a job by its UUID.
listJobs(request: ListJobsRequest): Promise<ListJobsResponse>
- Lists jobs with filtering parameters.
createJob(request: CreateJobRequest): Promise<CreateJobResponse[]>
- Creates a new job with the provided content data and returns the id and status of the job that can be requested at a later point in time. If you need to await the result use
evaluateinstead.
evaluate(request: EvaluateRequest): Promise<AsyncIterable<EvaluateMessage | StreamError>>
- Evaluates each input and returns a stream of responses or errors. Order is not guaranteed, so using metadata or a contentHash is advised when sending multiple pieces to evaluate in a single call.
- Example:
const stream = client.evaluate({
contentData: [
{
metadata: { id: "1" },
content: { value: "Hello, world!", $case: "text" },
contentType: "text",
},
{
metadata: { id: "2" },
content: { value: Buffer.from("<BASE64_DATA>", "base64"), $case: "image" },
contentType: "image/png",
},
{
metadata: { id: "3" },
content: { value: "<IMAGE_URL>", $case: "imageUrl" },
contentType: "image/jpeg",
},
],
policyId: "00000000-0000-0000-0000-000000000000",
});
for await (const item of stream) {
if (item instanceof StreamError) throw item;
const { metadata, report } = item;
console.log({ id: metadata?.id, matches: report?.matches });
}beta.evaluateLabels(request: EvaluateLabelsRequest): Promise<EvaluateLabelsResponse>
- Labels v2 evaluation is currently beta and exposed through
client.beta.evaluateLabels. - Evaluates one content item against 1-10 explicit Labels v2 label version IDs.
- Requires Labels v2 to be enabled for your customer account.
- Results are returned in requested label version ID order.
- The standard response includes
contentHashand, for each result,labelId,labelVersionId,jobUuid, eitherevaluation(outcome,score,threshold) orerror, and no nested report. This is typically sufficient for most SDK uses. - Per-label failures are returned in the response as result errors; transport, authentication, and platform errors still reject the call.
- Set
includeEvaluationReport: trueonly when you need optional nested report data on successful per-label evaluations, such asreport.matches. - Example:
const response = await client.beta.evaluateLabels({
content: {
metadata: { id: "1" },
content: { value: "Hello, world!", $case: "text" },
contentType: "text",
},
labelVersionIds: ["00000000-0000-0000-0000-000000000000", "11111111-1111-1111-1111-111111111111"],
threshold: 0.5,
});
for (const result of response.results) {
if (result.error) {
console.log({
labelId: result.labelId,
labelVersionId: result.labelVersionId,
jobUuid: result.jobUuid,
error: result.error.message,
});
continue;
}
console.log({
labelId: result.labelId,
labelVersionId: result.labelVersionId,
jobUuid: result.jobUuid,
outcome: result.evaluation?.outcome,
score: result.evaluation?.score,
threshold: result.evaluation?.threshold,
});
}When includeEvaluationReport: true is set and the backend returns report data, result.evaluation?.report has the same shape as policy evaluation reports, including matches.
Generated LabelsService CRUD, version, draft, tag, batch, and dataset APIs are private and are not part of the public SDK surface.
Refusals
Under certain circumstances, the Clavata API will refuse to evaluate content as requested. There are only a few possible reasons for this:
- The content (image) matches a known hash in our CSAM database. This can be either an exact match or a perceptual hash match.
- The content (image) is in an unsupported format. Currently, Clavata supports
webp,pngandjpgimages. - The content was corrupt. Usually because the content was incomplete or the encoding (for images) was incorrect.
When a refusal occurs, a StreamError will be returned (for evaluate) or thrown (for createJob). You can check whether the error was caused by a refusal using the getRefusalReason() method on the StreamError type. If the error was not caused by a refusal, this method will return undefined. If the error is due to a refusal, a RefusalReason will be returned.
The RefusalReason type is a enum with strings matching one of:
"CSAM""UNSUPPORTED_IMAGE_FORMAT""INVALID_IMAGE"
There is also an "UNKNOWN" option, but this case indicates something went wrong decoding the "reason" information in the response.
import { RefusalReason } from "@clavata/sdk";
const stream = client.evaluate(...)
for await (const item of stream) {
if (item instanceof StreamError) {
const reason = item.getRefusalReason();
if (!reason) {
// Not a refusal, you can check the other possible identification methods like
if (item.isTransient()) {
// Transient error so we can retry
}
}
// It was a refusal, so we can use a switch statement to figure out why
switch (reason) {
case RefusalReason.CSAM:
// Handle CSAM appropriately
break;
case RefusalReason.UNSUPPORTED_IMAGE_FORMAT:
// Convert image and try again
break;
case RefusalReason.INVALID_IMAGE:
// data corruption, maybe try to re-download from CDN and try again?
break;
default:
// unknown reason for refusal
}
}
}Getting the Refusal Error
If you prefer, you can convert a StreamError into a RefusalError with the toRefusalError() method. Once again, if the error was not caused by a refusal, this method will return undefined. If, however, a refusal did occur the RefusalError type that is returned has the following interface that you can use to understand why:
interface RefusalError {
isCSAM(): boolean;
isUnsupportedImageFormat(): boolean;
isInvalidImage(): boolean;
isUnknown(): boolean;
getReason(): RefusalReason;
}
// So you can do this:
if (item instanceof StreamError) {
const refusalErr = item.toRefusalError();
if (refusalErr.isCSAM()) {
// Take action based on the content having been CSAM
}
}Which interface you use to determine the reason is up to you, they are functionally equivalent.
Refusals and createJob
When calling createJob, a promise is returned. If an error occurs, that error is automatically converted to a StreamError by the SDK and re-thrown. You can then catch the StreamError and check for refusals the same way.
Monorepo development
When working in the Olympus monorepo, see SDK workflow for unit tests, live smoke (examples/sdk), and publishing (below).
Contributing
Update the version with npm version <new_version> to the next SEMVER then run npm publish.
