@lotagate/sdk
v0.1.7
Published
Type-safe JavaScript and TypeScript client for the LotaGate AI gateway.
Maintainers
Readme
@lotagate/sdk
Official JavaScript and TypeScript SDK for the LotaGate AI gateway.
The SDK provides one typed client for the capabilities exposed by LotaGate. It keeps provider routing, provider credentials, usage metering, billing and provider-specific URLs behind the gateway API.
Highlights
- ESM-first JavaScript and TypeScript API.
- Node.js 22 or newer.
- Text generation, embeddings, image generation and image editing.
- Speech synthesis, audio transcription and audio translation.
- Video generation, operation polling and video download.
- Google-native
v1betatext, embedding, image, prediction and interactions APIs. - Function tools, tool choice, tool calls and tool results across supported gateway formats.
- Model discovery and model details.
- Typed gateway and SDK errors with request and correlation identifiers.
- Streaming support for text generation, speech synthesis and audio transcription.
- Multipart upload support for image and audio inputs.
- Bounded response and stream buffers, request timeouts and cancellation.
Text resources expose provider-compatible content types instead of a single
cross-provider multimodal abstraction. Anthropic native document blocks are
available through client.messages; Responses requests continue to use the
gateway's canonical input_file shape.
Table of contents
- Highlights
- Requirements
- Installation
- Quick start
- Client configuration
- API reference
- Error handling
- Cancellation and timeouts
- TypeScript and runtime notes
- Package and licensing
Requirements
- Node.js
22+. - An environment with the standard Fetch API,
FormData,Blob, streams,AbortController,TextEncoderandTextDecoderglobals. - A LotaGate API key.
The package is ESM-only. Use import syntax and ensure your application is
configured for ESM, for example with "type": "module" in package.json.
Installation
npm install @lotagate/sdkFor pnpm:
pnpm add @lotagate/sdkFor yarn:
yarn add @lotagate/sdkQuick start
Store the API key in a server-side environment variable. Do not expose it in a browser bundle, client-side application code or source control.
import { LotaGate } from '@lotagate/sdk';
const client = new LotaGate({
apiKey: process.env.LOTA_GATE_API_KEY!,
});
const response = await client.chat.completions.create({
model: 'your-text-model',
messages: [
{ role: 'user', content: 'Explain how an API gateway helps application teams.' },
],
});
console.log(response.choices[0]?.message.content);The default production gateway is:
https://api.lotagate.comClient configuration
import { LotaGate } from '@lotagate/sdk';
const client = new LotaGate({
apiKey: process.env.LOTA_GATE_API_KEY!,
baseURL: 'https://api.lotagate.com',
timeoutMs: 60_000,
defaultHeaders: {
'x-application-name': 'my-service',
},
});| Option | Default | Purpose |
| --- | --- | --- |
| apiKey | Required | Bearer token sent to the gateway. |
| baseURL | https://api.lotagate.com | Gateway base URL. |
| timeoutMs | 60000 | Default request timeout. |
| defaultHeaders | None | Headers added to every request. |
| fetch | Runtime fetch | Optional fetch implementation for tests or custom runtimes. |
Each resource method also accepts request options:
const response = await client.models.list({
timeoutMs: 10_000,
signal: abortController.signal,
headers: { 'x-request-purpose': 'model-selection' },
});Only apiKey is required when creating the client. defaultHeaders is
optional and is intended for application metadata, tracing or similar headers;
the SDK always manages the Authorization header from apiKey.
Gateway payload limits
The SDK validates JSON and media file/aggregate payloads before sending them using the same limits as the public gateway contract. The server remains authoritative for the complete raw multipart envelope:
| Payload | Limit |
| --- | ---: |
| JSON request body on /v1 and /v1beta | 25 MB decimal |
| Audio file for transcription or translation | 50 MB decimal |
| Audio multipart request envelope | 70 MB decimal raw request body |
| Image edit: each image | 47 MB decimal |
| Image edit: mask | 4 MB decimal |
| Image edit: total images and mask | 128 MB decimal |
| Image edit: number of images | 16 |
| Image-edit multipart request envelope | 150 MB decimal raw request body |
These limits are not configurable through the SDK. The server remains the authoritative validator, and provider-specific limits may be stricter after the gateway resolves a model.
The JSON limit is measured on the serialized UTF-8 request body, including the prompt, history, tool definitions/results, and inline Base64 media. Multipart file, aggregate-file, and raw-request limits are separate. Raw multipart envelope limits include boundaries and form metadata; provider limits may still be stricter after the gateway resolves a model.
The SDK also has fixed internal response and SSE safety guardrails to bound buffering and stream parsing. They are implementation details and are not exposed as client options.
API reference
The client exposes these resource groups:
client.chat.completions
client.messages
client.responses
client.embeddings
client.images.generations
client.images.edits
client.audio.speech
client.audio.transcriptions
client.audio.translations
client.video.generations
client.video.operations
client.google.models
client.google.interactions
client.google.operations
client.modelsText generation
Chat completions
const response = await client.chat.completions.create({
model: 'your-text-model',
messages: [
{ role: 'system', content: 'You are a concise technical assistant.' },
{ role: 'user', content: 'Summarize this incident report.' },
],
temperature: 0.2,
max_tokens: 500,
});Method: client.chat.completions.create(request, options?)
The request requires model and messages. The supported common fields are
temperature, top_p, max_tokens, max_completion_tokens,
max_output_tokens, stop, stop_sequences, tools, tool_choice,
toolChoice, toolConfig, reasoning_effort and stream.
Use reasoning_effort to control the model's reasoning effort for the request:
const response = await client.chat.completions.create({
model: 'your-reasoning-model',
messages: [{ role: 'user', content: 'Analyze this incident.' }],
reasoning_effort: 'high',
});Function tools
Tools use the gateway's normalized function-tool contract. Tool calls returned
by the model are available on message.tool_calls and streaming deltas.
const response = await client.chat.completions.create({
model: 'your-text-model',
messages: [{ role: 'user', content: 'What is the weather in Hanoi?' }],
tools: [{
type: 'function',
function: {
name: 'get_weather',
description: 'Get current weather for a city.',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
},
}],
tool_choice: 'auto',
});
const toolCall = response.choices[0]?.message.tool_calls?.[0];To send the tool result back, append an assistant message with
tool_calls, followed by a role: 'tool' message with the matching
tool_call_id. The same ToolDefinition type accepts the OpenAI, Anthropic
and Google tool declaration shapes supported by the gateway. The SDK validates
and normalizes these forms through one shared utility before sending them to
the normalized text resources and Google Interactions. Google-native
generateContent keeps Google's native payload unchanged, so use its
functionDeclarations shape there.
Messages
const response = await client.messages.create({
model: 'your-text-model',
system: 'You are a helpful assistant.',
messages: [{ role: 'user', content: 'Write a short release note for this change.' }],
temperature: 0.4,
});Method: client.messages.create(request, options?)
The request requires model and messages. Use system for a system
instruction. This resource follows the server's Anthropic-compatible message
contract, including tool-use, tool-result and native document content blocks.
Anthropic documents are sent inline through content without the Files API:
const response = await client.messages.create({
model: 'your-anthropic-model',
messages: [{
role: 'user',
content: [
{
type: 'document',
source: {
type: 'text',
media_type: 'text/plain',
data: 'Hello from the attached document.',
},
},
{ type: 'text', text: 'Summarize the document.' },
],
}],
});The supported inline document sources are text, base64 and url. Use
text/* media types with text; use application/pdf with PDF base64 or
url sources. file_id and the Anthropic Files API are not part of this SDK
contract.
Responses
const response = await client.responses.create({
model: 'your-text-model',
instructions: ['Answer in three bullet points.', { type: 'input_text', text: 'Use plain language.' }],
input: [{ role: 'user', content: 'What are the main benefits of typed API clients?' }],
tools: [{
type: 'function',
function: {
name: 'get_weather',
description: 'Get current weather for a city.',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
strict: true,
},
}],
tool_choice: { type: 'function', name: 'get_weather' },
});Method: client.responses.create(request, options?)
The request requires model and input. input can be a string or a message
array. For tool-enabled turns it can also contain Responses function-call and
function-call-output items, allowing a tool result to be sent back with its
call_id. instructions can be a string or an array of strings/text items.
Responses function tools accept the shared function definition shape and are
normalized to the native flat payload (name, description, parameters, and
optional strict). tool_choice can select a function with the native
{ type: 'function', name: '...' } form or the shared nested function form.
Responses usage may expose input_tokens and output_tokens in addition to
the common token fields.
Responses does not use Anthropic native document blocks. For file content in
client.responses.create, use the canonical input_file content part with
file_data or file_url and an optional filename.
Streaming text responses
Set stream: true to receive an async iterable. The SDK parses SSE events and
handles chunk boundaries for you.
const stream = client.chat.completions.create({
model: 'your-text-model',
messages: [{ role: 'user', content: 'Explain rate limiting in one paragraph.' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta.content ?? '');
}The same streaming pattern is available for:
client.messages.create({
model: 'your-text-model',
messages: [{ role: 'user', content: 'Stream a message response.' }],
stream: true,
});
client.responses.create({
model: 'your-text-model',
input: 'Stream a response.',
stream: true,
});Those resources return AsyncIterable<TextStreamEvent>.
Google-native v1beta
Use client.google when the application needs Google's native payload shape
instead of the normalized /v1 gateway contract.
const model = await client.google.models.retrieve('your-text-model');
const response = await client.google.models.generateContent('your-text-model', {
contents: [{
role: 'user',
parts: [{ text: 'Explain vector search in one paragraph.' }],
}],
generationConfig: {
temperature: 0.2,
maxOutputTokens: 300,
},
});Available model operations are:
client.google.models.retrieve(modelCode, options?)client.google.models.generateContent(modelCode, request, options?)client.google.models.streamGenerateContent(modelCode, request, options?)client.google.models.embedContent(modelCode, request, options?)client.google.models.generateImages(modelCode, request, options?)client.google.models.predict(modelCode, request, options?)client.google.models.predictLongRunning(modelCode, request, options?)
The prediction methods use the server's native instances and parameters
fields. Long-running predictions return a Google operation:
const operation = await client.google.models.predictLongRunning('your-video-model', {
instances: [{ prompt: 'A slow sunrise over a quiet coastal town.' }],
parameters: { aspectRatio: '16:9' },
});
const status = await client.google.operations.retrieve(operation.name);
if (status.done) {
const video = await client.google.operations.downloadVideo(operation.name);
}Google Interactions are available through client.google.interactions.create:
const interaction = await client.google.interactions.create({
model: 'your-text-model',
input: 'What is the weather in Hanoi?',
tools: [{
type: 'function',
name: 'get_weather',
description: 'Get current weather for a city.',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
}],
tool_choice: 'auto',
});Set stream: true to receive an async iterable of native stream events. Use
client.google.operations.downloadVideo(operationId) for completed video
operations.
The existing client.video resource is the concise video-specific alias for
the supported :generateVideos and operation video routes.
Embeddings
const response = await client.embeddings.create({
model: 'your-embedding-model',
input: [
'First document to index.',
'Second document to index.',
],
encoding_format: 'float',
dimensions: 1536,
});
const vectors = response.data.map((item) => item.embedding);Method: client.embeddings.create(request, options?)
Required fields are model and input. input can be a string, string array,
token array or token-array batch. Optional fields include encoding_format,
dimensions and user.
Images
Image generation
const response = await client.images.generations.create({
model: 'your-image-model',
prompt: 'A clean editorial illustration of a secure API gateway.',
n: 1,
size: '1024x1024',
quality: 'high',
response_format: 'b64_json',
});
const image = response.data[0];Method: client.images.generations.create(request, options?)
Required fields are model and prompt. Supported fields are n, size,
quality, background, output_format, output_compression, moderation,
user and response_format.
Image editing
Image edits use multipart form data. The image field accepts one file or an
array of files. Upload values can be Blob, File, ArrayBuffer or
Uint8Array.
import { readFile } from 'node:fs/promises';
const sourceImage = await readFile('./assets/source.png');
const mask = await readFile('./assets/mask.png');
const response = await client.images.edits.create({
model: 'your-image-model',
prompt: 'Replace the background with a soft blue gradient.',
image: sourceImage,
mask,
filename: 'source.png',
n: 1,
size: '1024x1024',
quality: 'high',
background: 'transparent',
output_format: 'png',
output_compression: 80,
moderation: 'low',
user: 'internal-image-service',
input_fidelity: 'high',
response_format: 'b64_json',
});Method: client.images.edits.create(request, options?)
Required fields are model, prompt and image. The optional fields are
mask, filename, n, size, quality, background, output_format,
output_compression, moderation, user, input_fidelity and
response_format.
Audio
Speech synthesis
const audio = await client.audio.speech.create({
model: 'your-speech-model',
input: 'Your deployment completed successfully.',
voice: 'default',
instructions: 'Speak clearly and professionally.',
response_format: 'mp3',
speed: 1,
});
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('./output.mp3', new Uint8Array(audio.data)),
);Method: client.audio.speech.create(request, options?)
The request requires model, input and voice. Optional fields are
instructions, response_format, speed and stream_format. Without
stream_format: 'sse', it returns an object containing audio data as an
ArrayBuffer and response headers.
Streaming speech
Set stream_format: 'sse' to receive AsyncIterable<AudioSpeechStreamEvent>:
const events = client.audio.speech.create({
model: 'your-speech-model',
input: 'Stream this audio response.',
voice: 'default',
stream_format: 'sse',
});
for await (const event of events) {
console.log(event);
}The camelCase alias streamFormat: 'sse' is also accepted when that naming
style is preferred.
Transcription
import { readFile } from 'node:fs/promises';
const transcript = await client.audio.transcriptions.create({
model: 'your-transcription-model',
file: await readFile('./assets/meeting.wav'),
filename: 'meeting.wav',
language: 'en',
prompt: 'Use proper product names and punctuation.',
response_format: 'verbose_json',
temperature: 0.1,
timestamp_granularities: ['segment'],
include: ['logprobs'],
});
console.log(transcript.text);Method: client.audio.transcriptions.create(request, options?)
Required fields are model and file. Supported optional fields are
filename, language, prompt, response_format, temperature,
timestamp_granularities, include and stream.
Streaming transcription
Set stream: true to receive AsyncIterable<AudioTranscriptionStreamEvent>:
const events = client.audio.transcriptions.create({
model: 'your-transcription-model',
file: await readFile('./assets/meeting.wav'),
filename: 'meeting.wav',
response_format: 'verbose_json',
stream: true,
});
for await (const event of events) {
console.log(event);
}Translation
const translation = await client.audio.translations.create({
model: 'your-translation-model',
file: await readFile('./assets/meeting.wav'),
filename: 'meeting.wav',
prompt: 'Preserve technical terminology.',
response_format: 'text',
temperature: 0.2,
});
console.log(translation.text);Method: client.audio.translations.create(request, options?).
Required fields are model and file. Supported optional fields are
filename, prompt, response_format and temperature.
Video
Start a generation
Video generation returns an asynchronous operation. Keep the operation name and poll it until it is complete.
const operation = await client.video.generations.create({
model: 'your-video-model',
instances: [{ prompt: 'A slow aerial shot over a quiet coastal town at sunrise.' }],
parameters: {
aspectRatio: '16:9',
},
});
console.log(operation.name);Method: client.video.generations.create(request, options?).
The request requires model and instances. Each instance requires prompt.
Provider-compatible values can be passed through parameters.
Retrieve operation status and download the video
const operationId = operation.name;
const status = await client.video.operations.retrieve(operationId);
if (status.done) {
const video = await client.video.operations.download(operationId);
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('./output.mp4', new Uint8Array(video.data)),
);
}Methods:
client.video.operations.retrieve(operationId, options?)client.video.operations.download(operationId, options?)
The operation identifier must be a Google UUID v4. It may be supplied either as
the UUID itself or as a gateway operation name such as
operations/123e4567-e89b-42d3-a456-426614174000.
Models
const models = await client.models.list();
const model = await client.models.retrieve('your-text-model');
console.log(models.data);
console.log(model.id);Methods:
client.models.list(options?)client.models.retrieve(modelCode, options?)
Model codes are URL-encoded by the SDK. An empty model code is rejected before the request is sent.
Error handling
Gateway failures and SDK transport failures are represented by
LotaGateError.
import { LotaGateError, SdkErrorCode } from '@lotagate/sdk';
try {
await client.models.retrieve('your-model');
} catch (error: unknown) {
if (error instanceof LotaGateError) {
console.error({
statusCode: error.statusCode,
errorCode: error.errorCode,
requestId: error.requestId,
correlationId: error.correlationId,
details: error.details,
});
if (error.errorCode === SdkErrorCode.RequestTimeout) {
// Retry only when the operation is safe and the application policy allows it.
}
}
throw error;
}LotaGateError can expose:
statusCode: HTTP status, or0for a failure before an HTTP response.errorCode: gateway error code or an SDK error code.requestId: request identifier returned by the gateway.correlationId: correlation identifier returned by the gateway.details: structured gateway error details, when available.
SDK transport error codes are exported through SdkErrorCode:
| Code | Meaning |
| --- | --- |
| REQUEST_TIMEOUT | The request exceeded its timeout. |
| REQUEST_ABORTED | The caller cancelled the request. |
| NETWORK_ERROR | The request could not reach the gateway. |
| REQUEST_SERIALIZATION_ERROR | The request body could not be serialized. |
| REQUEST_VALIDATION_ERROR | The SDK rejected an invalid request before sending it. |
| REQUEST_PAYLOAD_TOO_LARGE | A request exceeded a gateway payload limit. |
| RESPONSE_BODY_READ_ERROR | The response body could not be read. |
| RESPONSE_PARSE_ERROR | The response or stream event was malformed. |
| RESPONSE_TOO_LARGE | A buffered response exceeded its limit. |
| STREAM_EVENT_TOO_LARGE | An SSE line or event exceeded its limit. |
| EMPTY_STREAM_RESPONSE | A stream response contained no usable events. |
Do not log API keys, uploaded file contents, prompts containing secrets or untrusted full response bodies. Use request and correlation IDs to investigate failures safely.
Cancellation and timeouts
Use an AbortController for caller-controlled cancellation:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5_000);
try {
await client.chat.completions.create(
{
model: 'your-text-model',
messages: [{ role: 'user', content: 'Generate a short status update.' }],
},
{ signal: controller.signal },
);
} finally {
clearTimeout(timeout);
}Prefer the SDK timeoutMs option for a standard request deadline and
AbortSignal when cancellation is tied to a request lifecycle or user action.
TypeScript and runtime notes
The SDK exports a curated set of request, response and stream contracts for typed wrappers and API integrations. Direct SDK calls do not require importing these types because TypeScript infers them from each client method.
Public type catalog
| Area | Public types |
| --- | --- |
| Client and errors | LotaGate, LotaGateCore, LotaGateClientOptions, RequestOptions, LotaGateErrorDetails, SdkErrorCodeValue |
| Common responses | BinaryResponse, Usage |
| Text generation | TextGenerationRequest, TextGenerationOptions, ReasoningEffort, TextMessage, TextMessageContent, TextContentPart, TextContentTextPart, TextImageContentPart, TextFileContentPart, AnthropicImageContentPart, AnthropicDocumentSource, AnthropicDocumentContentPart, AnthropicMessageContentPart, AnthropicMessageContent, MessageRequestMessage, AnthropicToolResultContentPart, AnthropicToolUseContentPart, GoogleFunctionCallContentPart, GoogleInlineDataContentPart, TextMessageDelta, TextRole, AssistantResponseMessage, AssistantTextMessage, UserTextMessage, ToolTextMessage, ToolDefinition, OpenAiFunctionTool, NamedToolDefinition, GoogleToolDefinition, GoogleInteractionToolDefinition, FunctionToolDefinition, GoogleFunctionDeclaration, JsonSchema, ToolChoice, ToolChoiceMode, ToolChoiceModeObject, NamedToolChoice, FunctionToolChoice, GoogleToolChoice, ToolCall, ChatCompletionRequest, ChatCompletionResponse, ChatCompletionChoice, ChatCompletionChunk, ChatCompletionChunkChoice, MessageRequest, MessageResponse, ResponseInput, ResponseInstruction, ResponseRequest, ResponseResponse, ResponseOutput, TextStreamEvent |
| Embeddings | EmbeddingInput, EmbeddingRequest, EmbeddingResponse, EmbeddingItem |
| Images | ImageGenerationRequest, ImageGenerationResponse, ImageResult, ImageEditRequest, ImageQuality, ImageBackground, ImageOutputFormat, ImageInputFidelity, UploadFile |
| Audio | SpeechRequest, SpeechResponseFormat, SpeechStreamFormat, AudioSpeechStreamEvent, AudioTranscriptionRequest, AudioTranscriptionResponseFormat, AudioTranscriptionStreamEvent, AudioTranslationRequest, AudioTranslationResponseFormat, AudioTimestampGranularity, AudioTextResponse |
| Video and Google | VideoGenerationRequest, VideoPromptInstance, VideoOperation, GoogleContent, GoogleContentPart, GoogleSystemInstruction, GoogleGenerationConfig, GoogleNativePayload, GoogleGenerateContentRequest, GoogleEmbedContentRequest, GooglePredictionRequest, GoogleInteractionInput, GoogleInteractionRequest, GoogleModel, GoogleOperation, GoogleStreamEvent |
| Models | ModelListResponse, ModelSummary |
For example, a wrapper can reuse the public request and response contracts:
import { LotaGate } from '@lotagate/sdk';
import type {
ChatCompletionRequest,
ChatCompletionResponse,
} from '@lotagate/sdk';
export async function createChat(
client: LotaGate,
request: ChatCompletionRequest & { stream?: false },
): Promise<ChatCompletionResponse> {
return client.chat.completions.create(request);
}Low-level transport, multipart, validation, parser and internal base types are not part of the public package contract.
The SDK uses the standard Web platform interfaces for uploads and responses.
For Node.js file uploads, Uint8Array values from node:fs/promises can be
passed directly, as shown in the examples.
Package and licensing
- Package:
@lotagate/sdk - License: LotaGate Proprietary Software License v1.0 (see
LICENSE.md) - Website: LotaGate
- Support and bug reports: Contact LotaGate
