@aiand/sdk
v0.1.0
Published
TypeScript SDK for the ai& API, generated from the public OpenAPI spec.
Maintainers
Readme
aiand-typescript
Use the ai& API with TypeScript.
This package is generated from the public ai& OpenAPI spec with OpenAPI Generator. It covers the OpenAI-compatible endpoints currently present in the spec: models, chat completions, legacy completions, responses, files, and chunked uploads.
The npm package name is @aiand/sdk.
Installation
From this checkout:
cd aiand-typescript
npm install
npm run buildOnce this package is published, install it as:
npm install @aiand/sdkThe current SDK version is 0.1.0. See CHANGELOG.md for release notes.
Usage
Set your API key in the environment:
export AIAND_API_KEY="your-aiand-api-key"Create a client:
import { Configuration, OpenaiApi } from "@aiand/sdk";
const configuration = new Configuration({
accessToken: process.env.AIAND_API_KEY,
});
const client = new OpenaiApi(configuration);
const models = await client.listModels();
console.log(models.data[0].id);The generated base URL is https://api.aiand.com. The OpenAPI paths include /v1, so SDK
calls resolve to URLs like https://api.aiand.com/v1/models.
OpenaiApi is generated from the source spec's openai tag.
Chat
import { Configuration, OpenaiApi } from "@aiand/sdk";
const client = new OpenaiApi(
new Configuration({ accessToken: process.env.AIAND_API_KEY }),
);
const response = await client.createChatCompletion({
createChatCompletionRequest: {
model: "openai/gpt-oss-120b",
messages: [
{ role: "system", content: "You are concise and practical." },
{ role: "user", content: "Give me one sentence about ai&." },
],
temperature: 0.2,
},
});
console.log(response.choices[0].message.content);Completions
import { Configuration, OpenaiApi } from "@aiand/sdk";
const client = new OpenaiApi(
new Configuration({ accessToken: process.env.AIAND_API_KEY }),
);
const response = await client.createCompletion({
createCompletionRequest: {
model: "openai/gpt-oss-120b",
prompt: "Write a short product tagline for ai&:",
max_tokens: 32,
},
});
console.log(response.choices[0].text);Responses
import { Configuration, OpenaiApi } from "@aiand/sdk";
const client = new OpenaiApi(
new Configuration({ accessToken: process.env.AIAND_API_KEY }),
);
const response = await client.createResponse({
createResponseRequest: {
model: "openai/gpt-oss-120b",
input: "Give me one practical sentence about ai&.",
temperature: 0.2,
max_output_tokens: 64,
parallel_tool_calls: false,
truncation: "disabled",
},
});
console.log(response.output);Models And Pricing
import { Configuration, OpenaiApi } from "@aiand/sdk";
const client = new OpenaiApi(
new Configuration({ accessToken: process.env.AIAND_API_KEY }),
);
const models = await client.listModels();
for (const model of models.data) {
console.log(
model.id,
model.provider,
model.context_window,
model.capabilities,
model.input_per_1m,
model.output_per_1m,
);
}The docs describe model pricing as precise string fields. This SDK keeps
input_per_1m and output_per_1m as strings instead of numbers.
Files
Upload a file once, then reference the returned file_id from chat completion requests.
import { readFile } from "node:fs/promises";
import { Configuration, FilesApi } from "@aiand/sdk";
const files = new FilesApi(
new Configuration({ accessToken: process.env.AIAND_API_KEY }),
);
const bytes = await readFile("diagram.png");
const file = new Blob([bytes], { type: "image/png" });
const uploaded = await files.uploadFile({
file,
purpose: "vision",
});
console.log(uploaded.id);The file purpose values are vision, video, audio, and document.
Chunked Uploads
For larger assets, create an upload, add parts in order, then complete it.
import { readFile } from "node:fs/promises";
import { Configuration, UploadsApi } from "@aiand/sdk";
const uploads = new UploadsApi(
new Configuration({ accessToken: process.env.AIAND_API_KEY }),
);
const bytes = await readFile("clip.mp4");
const upload = await uploads.createUpload({
createUploadRequest: {
filename: "clip.mp4",
purpose: "video",
bytes: bytes.byteLength,
mime_type: "video/mp4",
},
});
const part = await uploads.addUploadPart({
id: upload.id,
data: new Blob([bytes], { type: "video/mp4" }),
});
const completed = await uploads.completeUpload({
id: upload.id,
completeUploadRequest: { part_ids: [part.id] },
});
console.log(completed.file?.id);Timeouts And Headers
Every generated operation accepts the standard fetch override argument:
const response = await client.listModels({
signal: AbortSignal.timeout(30_000),
headers: { "X-Request-Source": "aiand-typescript" },
});Use Configuration({ accessToken: ... }) for API-key auth. The docs note that browser/JWT
auth can require X-Org-ID; for server-side API keys, the organization is resolved from
the key.
Errors
The generated client throws ResponseError for non-2xx responses.
import { ResponseError } from "@aiand/sdk";
try {
await client.listModels();
} catch (error) {
if (error instanceof ResponseError) {
console.log(error.response.status);
console.log(await error.response.text());
}
}Testing
Run the unit tests without making network calls:
npm testRun the focused type check:
npm run lintBuild the package:
npm run buildGenerated code under src/ is recreated by OpenAPI Generator and should be reviewed for
behavior, not reformatted by hand.
Recording HTTP Cassettes
Tests use PollyJS for VCR-style live API coverage.
Polly records HAR fixtures under its default recordings directory.
Committed recordings are replayed by default when you run:
npm testTo refresh or add recordings, export an API key and run the focused VCR suite:
export AIAND_API_KEY=your-aiand-api-key
npm run test:vcrThe cassette config filters the request Authorization header and the same response
headers as the Python SDK before HAR fixtures are written. Only commit sanitized cassette
files.
The VCR suite records one Polly HAR cassette per public endpoint group:
list-modelschat-completioncompletionresponsefiles-lifecycleuploads-completeuploads-cancel
Together those cassettes hit every endpoint currently generated from the OpenAPI spec:
GET /v1/models, POST /v1/chat/completions, POST /v1/completions,
POST /v1/responses, GET /v1/files, POST /v1/files, GET /v1/files/{id},
GET /v1/files/{id}/content, DELETE /v1/files/{id}, POST /v1/uploads,
POST /v1/uploads/{id}/parts, POST /v1/uploads/{id}/complete, and
POST /v1/uploads/{id}/cancel.
Updating The SDK
Prerequisites:
- Java, required by OpenAPI Generator.
- Node/npm with
npx, used to run@openapitools/[email protected]. - Node 18 or newer.
Regenerate from the latest published spec:
./scripts/update-sdkThat script:
- Downloads
https://api.aiand.com/openapi.jsontoopenapi/openapi.json. - Runs OpenAPI Generator with
openapi-generator-config.yaml. - Applies
scripts/patch-generated-client.mjsfor generator-specific TypeScript compatibility.
After regenerating:
npm test
npm run lint
npm run buildReview the generated diff in src/, docs/, and openapi/openapi.json.
The npm wrapper is pinned in scripts/update-sdk, and the OpenAPI Generator version is
pinned in openapitools.json. To upgrade either one, edit the pinned version, regenerate,
and review the generated diff carefully.
Development Notes
Most SDK files are generated. The main hand-maintained files are:
README.mdCHANGELOG.mdLICENSEpackage.jsontsconfig.jsonvitest.config.tsscripts/update-sdkscripts/patch-generated-client.mjstests/
scripts/patch-generated-client.mjs patches the generated ChatCompletionMessage type.
The current spec models chat message variants with anonymous anyOf schemas, and the
TypeScript generator collapses the aggregate type into the tool message shape. The patch
keeps normal system, user, assistant, developer, and tool messages type-safe
until the source spec names or discriminates those variants.
Bug reports and pull requests are welcome.
License
This project is licensed under the Apache License 2.0.
