diff --git a/CHANGELOG.md b/CHANGELOG.md index 6106b84..32e4210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/package.json b/package.json index 8266741..944ab0a 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/types/llm-gateway/index.ts b/src/types/llm-gateway/index.ts index 7d8fb57..ec96e7f 100644 --- a/src/types/llm-gateway/index.ts +++ b/src/types/llm-gateway/index.ts @@ -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; @@ -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; @@ -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; }; @@ -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 = { diff --git a/tests/unit/llm-gateway.test.ts b/tests/unit/llm-gateway.test.ts index db98e26..69be57e 100644 --- a/tests/unit/llm-gateway.test.ts +++ b/tests/unit/llm-gateway.test.ts @@ -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); + }); });