Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/scribe-enable-logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@elevenlabs/client": minor
"@elevenlabs/react": minor
---

Add `enableLogging` option (`boolean`) to the Scribe realtime API, available on `Scribe.connect` and the `useScribe` hook. Setting it to `false` sends `enable_logging=false` on the WebSocket URL, which runs the session in zero retention mode so history features are unavailable for it. Zero retention mode may only be used by enterprise customers.
5 changes: 5 additions & 0 deletions .changeset/scribe-session-started-enable-logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@elevenlabs/types": minor
---

Rename `disable_logging` to `enable_logging` in the Scribe `session_started` config to match the field the server actually reports. `disable_logging` was never sent on the wire.
62 changes: 61 additions & 1 deletion packages/client/src/scribe/scribe.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { it, expect, describe, vi, beforeEach } from "vitest";
import { it, expect, describe, vi, beforeEach, onTestFinished } from "vitest";
import { Server } from "mock-socket";
import type { Client } from "mock-socket";
import {
Expand All @@ -18,6 +18,31 @@ const TEST_MODEL_ID = "scribe_v2_realtime";
const TEST_SESSION_ID = "test-session-id";
const PARTIAL_TRANSCRIPT_TEXT = "Hello, this is a partial";
const COMMITTED_TRANSCRIPT_TEXT = "Hello, this is a committed transcript.";
const SCRIBE_WS_URL = "wss://api.elevenlabs.io/v1/speech-to-text/realtime";

/**
* Starts a mock Scribe server and resolves with the query params the client
* connected with. The mock server matches on the URL without its query part, so
* inspecting the client URI is the only way to assert on the URI that was built.
* Call before connecting so the connection listener is already attached.
*/
function connectionQuery(): Promise<URLSearchParams> {
const server = new Server(SCRIBE_WS_URL);
onTestFinished(() => server.close());

return new Promise((resolve, reject) => {
const timeout = setTimeout(
() => reject(new Error("timed out waiting for connection")),
5000
);
onTestFinished(() => clearTimeout(timeout));

server.on("connection", socket =>
resolve(new URL(socket.url).searchParams)
);
server.on("error", reject);
});
}

describe("Scribe", () => {
describe("WebSocket URI Building", () => {
Expand Down Expand Up @@ -256,6 +281,41 @@ describe("Scribe", () => {
server.close();
});

it.each([
{ enableLogging: false, expected: "false" },
{ enableLogging: true, expected: "true" },
])(
"builds URI with enable_logging=$expected when enableLogging is $enableLogging",
async ({ enableLogging, expected }) => {
const query = connectionQuery();

const connection = Scribe.connect({
token: TEST_TOKEN,
modelId: TEST_MODEL_ID,
audioFormat: AudioFormat.PCM_16000,
sampleRate: 16000,
enableLogging,
});
onTestFinished(() => connection.close());

expect((await query).get("enable_logging")).toBe(expected);
}
);

it("omits enable_logging when enableLogging is not set", async () => {
const query = connectionQuery();

const connection = Scribe.connect({
token: TEST_TOKEN,
modelId: TEST_MODEL_ID,
audioFormat: AudioFormat.PCM_16000,
sampleRate: 16000,
});
onTestFinished(() => connection.close());

expect((await query).has("enable_logging")).toBe(false);
});

it("accepts valid parameter values", () => {
const server = new Server(
"wss://api.elevenlabs.io/v1/speech-to-text/realtime?model_id=scribe_v2_realtime&token=sutkn_123&vad_silence_threshold_secs=1.5&vad_threshold=0.5&min_speech_duration_ms=100&min_silence_duration_ms=200"
Expand Down
11 changes: 11 additions & 0 deletions packages/client/src/scribe/scribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ interface BaseOptions {
* @default false
*/
noVerbatim?: boolean;
/**
* Whether the request may be logged by ElevenLabs.
* When set to false, zero retention mode is used for the session, which means
* history features are unavailable for it. Zero retention mode may only be
* used by enterprise customers.
* @default true
*/
enableLogging?: boolean;
}

export interface AudioOptions extends BaseOptions {
Expand Down Expand Up @@ -201,6 +209,9 @@ export class ScribeRealtime {
if (options.noVerbatim !== undefined) {
params.append("no_verbatim", options.noVerbatim ? "true" : "false");
}
if (options.enableLogging !== undefined) {
params.append("enable_logging", options.enableLogging ? "true" : "false");
}

const queryString = params.toString();
return queryString ? `${baseUri}?${queryString}` : baseUri;
Expand Down
38 changes: 38 additions & 0 deletions packages/react/src/scribe.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,42 @@ describe("useScribe", () => {
})
);
});

it("passes enableLogging through to the client", async () => {
const { result } = renderHook(() => useScribe({ enableLogging: false }));

await act(async () => {
await result.current.connect({
token: "test-token",
modelId: "scribe_v2_realtime",
audioFormat: AudioFormat.PCM_16000,
sampleRate: 16000,
});
});

expect(Scribe.connect).toHaveBeenCalledWith(
expect.objectContaining({
enableLogging: false,
})
);
});

it("lets connect() disable logging for a session enabled at the hook level", async () => {
const { result } = renderHook(() => useScribe({ enableLogging: true }));

await act(async () => {
await result.current.connect({
token: "test-token",
modelId: "scribe_v2_realtime",
microphone: {},
enableLogging: false,
});
});

expect(Scribe.connect).toHaveBeenCalledWith(
expect.objectContaining({
enableLogging: false,
})
);
});
});
16 changes: 16 additions & 0 deletions packages/react/src/scribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ export interface ScribeHookOptions extends ScribeCallbacks {
// Keyterms and verbatim control
keyterms?: string[];
noVerbatim?: boolean;

/**
* Whether the session may be logged by ElevenLabs. Set to `false` to use zero
* retention mode, which makes history features unavailable for the session.
* Zero retention mode may only be used by enterprise customers.
* @default true
*/
enableLogging?: boolean;
}

export interface UseScribeReturn {
Expand Down Expand Up @@ -199,6 +207,9 @@ export function useScribe(options: ScribeHookOptions = {}): UseScribeReturn {
// Keyterms and verbatim control
keyterms: defaultKeyterms,
noVerbatim: defaultNoVerbatim,

// Logging
enableLogging: defaultEnableLogging,
} = options;

const connectionRef = useRef<RealtimeConnection | null>(null);
Expand Down Expand Up @@ -258,6 +269,8 @@ export function useScribe(options: ScribeHookOptions = {}): UseScribeReturn {
const includeLanguageDetection =
runtimeOptions.includeLanguageDetection ??
defaultIncludeLanguageDetection;
const enableLogging =
runtimeOptions.enableLogging ?? defaultEnableLogging;

if (microphone) {
// Microphone mode
Expand All @@ -282,6 +295,7 @@ export function useScribe(options: ScribeHookOptions = {}): UseScribeReturn {
microphone,
includeTimestamps,
includeLanguageDetection,
enableLogging,
} as MicrophoneOptions);
} else if (audioFormat && sampleRate) {
// Manual audio mode
Expand All @@ -305,6 +319,7 @@ export function useScribe(options: ScribeHookOptions = {}): UseScribeReturn {
noVerbatim: runtimeOptions.noVerbatim ?? defaultNoVerbatim,
includeTimestamps,
includeLanguageDetection,
enableLogging,
audioFormat,
sampleRate,
} as AudioOptions);
Expand Down Expand Up @@ -492,6 +507,7 @@ export function useScribe(options: ScribeHookOptions = {}): UseScribeReturn {
defaultIncludeLanguageDetection,
defaultKeyterms,
defaultNoVerbatim,
defaultEnableLogging,
onSessionStarted,
onPartialTranscript,
onCommittedTranscript,
Expand Down
2 changes: 1 addition & 1 deletion packages/types/generated/types/asyncapi-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ export interface Config {
min_speech_duration_ms?: number;
min_silence_duration_ms?: number;
model_id?: string;
disable_logging?: boolean;
enable_logging?: boolean;
keyterms?: string[];
no_verbatim?: boolean;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/types/schemas/scribe.asyncapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,9 @@ components:
model_id:
type: string
description: ID of the model to use for transcription
disable_logging:
enable_logging:
type: boolean
description: Whether to disable logging
description: Whether the session may be logged. When false, zero retention mode is used and history features are unavailable for the session.
keyterms:
type: array
items:
Expand Down
Loading