Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [4.41.6]

- Expose `presence_penalty` in `ModelDefaultParameters` and `ChatCompletionRequest` — allows reading and configuring presence penalty for LLM Gateway models
- Expose `thinking` in `ChatCompletionMessage` — surfaces reasoning/thinking content for reasoning models on the LLM Gateway
- Expose detailed token breakdown fields (`prompt_tokens_details`, `completion_tokens_details`, `input_tokens`, `output_tokens`) in `ChatCompletionUsage` — surfaces cached tokens, cache creation, and reasoning token metrics
- Expose optional `http_status_code`, `response_time`, `llm_status_code`, and `request` in `ChatCompletionResponse`

## [4.41.5]

- Add `speakerLabelsRevisionIntervalMs` streaming param (sent as `speaker_labels_revision_interval_ms`) — the cadence, in ms of audio time, at which the server emits mid-stream `speakerRevision` events when `speakerLabels` is enabled. Unset or `0` sends only the end-of-stream revision; non-zero values are clamped server-side to 120 000–300 000 ms. The first mid-stream revision needs ~120 s of streamed speech, and one is sent only when some earlier turn's label changed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "assemblyai",
"version": "4.41.5",
"version": "4.41.6",
"description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription.",
"engines": {
"node": ">=18"
Expand Down
33 changes: 32 additions & 1 deletion src/types/llm-gateway/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export type ChatCompletionMessage = {
role: "user" | "assistant" | "system";
content?: string | { type: string; text: string }[];
thinking?: string | null;
tool_calls?: unknown[];
tool_call_id?: string;
name?: string;
Expand All @@ -21,6 +22,8 @@ export type ChatCompletionRequest = {
max_tokens?: number;
top_p?: number;
top_k?: number;
frequency_penalty?: number;
presence_penalty?: number;
tools?: unknown[];
tool_choice?: unknown;
response_format?: unknown;
Expand All @@ -29,15 +32,38 @@ export type ChatCompletionRequest = {
[key: string]: unknown;
};

export type ChatCompletionCacheCreation = {
ephemeral_5m_input_tokens?: number | null;
ephemeral_1h_input_tokens?: number | null;
};

export type ChatCompletionPromptTokensDetails = {
cached_tokens?: number | null;
audio_tokens?: number | null;
cache_creation?: ChatCompletionCacheCreation | null;
cache_write_tokens?: number | null;
};

export type ChatCompletionTokensDetails = {
reasoning_tokens?: number | null;
audio_tokens?: number | null;
accepted_prediction_tokens?: number | null;
rejected_prediction_tokens?: number | null;
};

export type ChatCompletionUsage = {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
input_tokens?: number | null;
output_tokens?: number | null;
prompt_tokens_details?: ChatCompletionPromptTokensDetails | null;
completion_tokens_details?: ChatCompletionTokensDetails | null;
};

export type ChatCompletionChoice = {
index: number;
finish_reason?: string;
finish_reason?: string | null;
message: ChatCompletionMessage;
};

Expand All @@ -48,12 +74,17 @@ export type ChatCompletionResponse = {
request_id: string;
choices: ChatCompletionChoice[];
usage: ChatCompletionUsage;
http_status_code?: number | null;
response_time?: number | null;
llm_status_code?: number | null;
request?: unknown;
};

export type ModelDefaultParameters = {
temperature?: number | null;
top_p?: number | null;
frequency_penalty?: number | null;
presence_penalty?: number | null;
};

export type ModelTopProvider = {
Expand Down
110 changes: 110 additions & 0 deletions tests/unit/llm-gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,114 @@ describe("llmGateway", () => {
requestId: "req_000",
});
});

it("should parse detailed usage token breakdowns and thinking in chat completions", async () => {
const detailedResponse = {
request_id: "req_detailed_123",
http_status_code: 200,
response_time: 1542000000,
llm_status_code: 200,
choices: [
{
index: 0,
finish_reason: "stop",
message: {
role: "assistant",
content: "Here is the summary.",
thinking: "First analyze key points...",
},
},
],
usage: {
input_tokens: 120,
output_tokens: 45,
prompt_tokens: 120,
completion_tokens: 45,
total_tokens: 165,
prompt_tokens_details: {
cached_tokens: 60,
audio_tokens: 0,
cache_creation: {
ephemeral_5m_input_tokens: 30,
ephemeral_1h_input_tokens: 0,
},
cache_write_tokens: 30,
},
completion_tokens_details: {
reasoning_tokens: 25,
audio_tokens: 0,
accepted_prediction_tokens: 0,
rejected_prediction_tokens: 0,
},
},
};
fetchMock.doMockOnceIf(
requestMatches({ url: "/v1/chat/completions", method: "POST" }),
JSON.stringify(detailedResponse),
);
const result = await assembly.llmGateway.chatCompletions({
model: "claude-haiku-4-5-20251001",
messages: [{ role: "user", content: "Summarize" }],
frequency_penalty: 0.5,
presence_penalty: 0.2,
});
expect(result.request_id).toBe("req_detailed_123");
expect(result.http_status_code).toBe(200);
expect(result.response_time).toBe(1542000000);
expect(result.choices[0].message.thinking).toBe(
"First analyze key points...",
);
expect(result.usage.prompt_tokens_details?.cached_tokens).toBe(60);
expect(
result.usage.prompt_tokens_details?.cache_creation
?.ephemeral_5m_input_tokens,
).toBe(30);
expect(result.usage.completion_tokens_details?.reasoning_tokens).toBe(25);
expect(requestBody()).toMatchObject({
frequency_penalty: 0.5,
presence_penalty: 0.2,
});
});

it("should parse presence_penalty in model default parameters", async () => {
const modelsWithPresencePenalty = {
data: [
{
id: "gpt-4o",
name: "GPT-4o",
description: "OpenAI GPT-4o",
default_parameters: {
temperature: 0.7,
top_p: 0.95,
frequency_penalty: 0.5,
presence_penalty: 0.2,
},
supported_parameters: [
"temperature",
"frequency_penalty",
"presence_penalty",
],
top_provider: {
is_moderated: true,
context_length: 128000,
max_completion_tokens: 4096,
},
context_length: 128000,
pricing: { global: { completions: 10.0, prompt: 2.5 } },
creator: "openai",
retirement_date: 0,
available_regions: ["global"],
providers: ["openai"],
default_provider: "openai",
},
],
};
fetchMock.doMockOnceIf(
requestMatches({ url: "/v1/models", method: "GET" }),
JSON.stringify(modelsWithPresencePenalty),
);
const result = await assembly.llmGateway.listModels();
expect(result.data[0].default_parameters.presence_penalty).toBe(0.2);
expect(result.data[0].default_parameters.frequency_penalty).toBe(0.5);
});
});