Skip to content

Commit f96eb99

Browse files
committed
align turn configurations
1 parent 9b36708 commit f96eb99

9 files changed

Lines changed: 74 additions & 48 deletions

File tree

agents/src/voice/agent.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import type { VAD } from '../vad.js';
3131
import type { AgentActivity } from './agent_activity.js';
3232
import type { AgentSession, TurnDetectionMode } from './agent_session.js';
3333
import type { TimedString } from './io.js';
34-
import type { InterruptionConfig } from './turn_config/interruption.js';
35-
import type { TurnHandlingConfig } from './turn_config/turn_handling.js';
34+
import type { InterruptionOptions } from './turn_config/interruption.js';
35+
import type { TurnHandlingOptions } from './turn_config/turn_handling.js';
3636
import { migrateLegacyOptions } from './turn_config/utils.js';
3737

3838
export const asyncLocalStorage = new AsyncLocalStorage<{ functionCall?: FunctionCall }>();
@@ -76,7 +76,7 @@ export interface AgentOptions<UserData> {
7676
tts?: TTS | TTSModelString;
7777
allowInterruptions?: boolean;
7878
minConsecutiveSpeechDelay?: number;
79-
turnHandling?: TurnHandlingConfig;
79+
turnHandling?: TurnHandlingOptions;
8080
useTtsAlignedTranscript?: boolean;
8181
}
8282

@@ -86,8 +86,8 @@ export class Agent<UserData = any> {
8686
private _vad?: VAD;
8787
private _llm?: LLM | RealtimeModel;
8888
private _tts?: TTS;
89-
private turnHandling?: TurnHandlingConfig;
90-
private _interruptionDetection: InterruptionConfig['mode'];
89+
private turnHandling?: TurnHandlingOptions;
90+
private _interruptionDetection: InterruptionOptions['mode'];
9191
private _allowInterruptions?: boolean;
9292
private _useTtsAlignedTranscript?: boolean;
9393

@@ -215,7 +215,7 @@ export class Agent<UserData = any> {
215215
return this.getActivityOrThrow().agentSession as AgentSession<UserData>;
216216
}
217217

218-
get interruptionDetection(): InterruptionConfig['mode'] {
218+
get interruptionDetection(): InterruptionOptions['mode'] {
219219
return this._interruptionDetection;
220220
}
221221

agents/src/voice/agent_session.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ import {
7171
import type { UnknownUserData } from './run_context.js';
7272
import type { SpeechHandle } from './speech_handle.js';
7373
import { RunResult } from './testing/run_result.js';
74-
import type { InterruptionConfig } from './turn_config/interruption.js';
74+
import type { InterruptionOptions } from './turn_config/interruption.js';
7575
import type {
76-
InternalTurnHandlingConfig,
77-
TurnHandlingConfig,
76+
InternalTurnHandlingOptions,
77+
TurnHandlingOptions,
7878
} from './turn_config/turn_handling.js';
7979
import { migrateLegacyOptions } from './turn_config/utils.js';
8080

@@ -103,7 +103,7 @@ export interface SessionOptions {
103103
/**
104104
* Configuration for turn handling.
105105
*/
106-
turnHandling: Partial<TurnHandlingConfig>;
106+
turnHandling: Partial<TurnHandlingOptions>;
107107

108108
useTtsAlignedTranscript: boolean;
109109

@@ -122,7 +122,7 @@ export interface SessionOptions {
122122
}
123123

124124
export interface InternalSessionOptions extends SessionOptions {
125-
turnHandling: InternalTurnHandlingConfig;
125+
turnHandling: InternalTurnHandlingOptions;
126126
}
127127

128128
export const defaultSessionOptions = {
@@ -207,7 +207,7 @@ export class AgentSession<
207207
private sessionSpan?: Span;
208208
private agentSpeakingSpan?: Span;
209209

210-
private _interruptionDetection?: InterruptionConfig['mode'];
210+
private _interruptionDetection?: InterruptionOptions['mode'];
211211

212212
private _usageCollector: ModelUsageCollector = new ModelUsageCollector();
213213

agents/src/voice/audio_recognition.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,13 @@ export interface RecognitionHooks {
5959
}
6060

6161
export interface _TurnDetector {
62+
/** The model name used by this turn detector. */
63+
readonly model: string;
64+
/** The provider name for this turn detector. */
65+
readonly provider: string;
6266
unlikelyThreshold: (language?: string) => Promise<number | undefined>;
6367
supportsLanguage: (language?: string) => Promise<boolean>;
64-
predictEndOfTurn(chatCtx: ChatContext): Promise<number>;
68+
predictEndOfTurn(chatCtx: ChatContext, timeout?: number): Promise<number>;
6569
}
6670

6771
export interface AudioRecognitionOptions {

agents/src/voice/turn_config/endpointing.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
/**
55
* Configuration for endpointing, which determines when the user's turn is complete.
66
*/
7-
export interface EndpointingConfig {
7+
export interface EndpointingOptions {
8+
/**
9+
* Endpointing mode. `"fixed"` uses a fixed delay, `"dynamic"` adjusts delay based on
10+
* end-of-utterance prediction.
11+
* @defaultValue "fixed"
12+
*/
13+
mode: 'fixed' | 'dynamic';
814
/**
915
* Minimum time in milliseconds since the last detected speech before the agent declares the user's
1016
* turn complete. In VAD mode this effectively behaves like `max(VAD silence, minDelay)`;
@@ -20,7 +26,8 @@ export interface EndpointingConfig {
2026
maxDelay: number;
2127
}
2228

23-
export const defaultEndpointingConfig = {
29+
export const defaultEndpointingOptions = {
30+
mode: 'fixed',
2431
minDelay: 500,
2532
maxDelay: 3000,
26-
} as const satisfies EndpointingConfig;
33+
} as const satisfies EndpointingOptions;

agents/src/voice/turn_config/interruption.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44
/**
55
* Configuration for interruption handling.
66
*/
7-
export interface InterruptionConfig {
7+
export interface InterruptionOptions {
88
/**
9-
* Interruption handling strategy.
9+
* Whether interruptions are enabled.
10+
* @defaultValue true
11+
*/
12+
enabled: boolean;
13+
/**
14+
* Interruption handling strategy. `"adaptive"` for ML-based detection, `"vad"` for simple
15+
* voice-activity detection. `undefined` means auto-detect.
1016
* @defaultValue undefined
1117
*/
1218
mode: 'adaptive' | 'vad' | false | undefined;
@@ -39,11 +45,12 @@ export interface InterruptionConfig {
3945
resumeFalseInterruption: boolean;
4046
}
4147

42-
export const defaultInterruptionConfig = {
48+
export const defaultInterruptionOptions = {
49+
enabled: true,
4350
mode: undefined,
4451
discardAudioIfUninterruptible: true,
4552
minDuration: 500,
4653
minWords: 0,
4754
falseInterruptionTimeout: 2000,
4855
resumeFalseInterruption: true,
49-
} as const satisfies InterruptionConfig;
56+
} as const satisfies InterruptionOptions;

agents/src/voice/turn_config/turn_handling.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
import type { TurnDetectionMode } from '../agent_session.js';
5-
import { type EndpointingConfig, defaultEndpointingConfig } from './endpointing.js';
6-
import { type InterruptionConfig, defaultInterruptionConfig } from './interruption.js';
5+
import { type EndpointingOptions, defaultEndpointingOptions } from './endpointing.js';
6+
import { type InterruptionOptions, defaultInterruptionOptions } from './interruption.js';
77

88
/**
99
* Configuration for the turn handling system. Used to configure the turn taking behavior of the
1010
* session.
1111
*/
12-
export interface TurnHandlingConfig {
12+
export interface TurnHandlingOptions {
1313
/**
1414
* Strategy for deciding when the user has finished speaking.
1515
*
@@ -26,20 +26,20 @@ export interface TurnHandlingConfig {
2626
/**
2727
* Configuration for endpointing.
2828
*/
29-
endpointing: Partial<EndpointingConfig>;
29+
endpointing: Partial<EndpointingOptions>;
3030
/**
3131
* Configuration for interruption handling.
3232
*/
33-
interruption: Partial<InterruptionConfig>;
33+
interruption: Partial<InterruptionOptions>;
3434
}
3535

36-
export interface InternalTurnHandlingConfig extends TurnHandlingConfig {
37-
endpointing: EndpointingConfig;
38-
interruption: InterruptionConfig;
36+
export interface InternalTurnHandlingOptions extends TurnHandlingOptions {
37+
endpointing: EndpointingOptions;
38+
interruption: InterruptionOptions;
3939
}
4040

41-
export const defaultTurnHandlingConfig: InternalTurnHandlingConfig = {
41+
export const defaultTurnHandlingOptions: InternalTurnHandlingOptions = {
4242
turnDetection: undefined,
43-
interruption: defaultInterruptionConfig,
44-
endpointing: defaultEndpointingConfig,
43+
interruption: defaultInterruptionOptions,
44+
endpointing: defaultEndpointingOptions,
4545
};

agents/src/voice/turn_config/utils.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
// SPDX-License-Identifier: Apache-2.0
44
import { beforeAll, describe, expect, it } from 'vitest';
55
import { initializeLogger } from '../../log.js';
6-
import { defaultEndpointingConfig } from './endpointing.js';
7-
import { defaultInterruptionConfig } from './interruption.js';
8-
import { defaultTurnHandlingConfig } from './turn_handling.js';
6+
import { defaultEndpointingOptions } from './endpointing.js';
7+
import { defaultInterruptionOptions } from './interruption.js';
8+
import { defaultTurnHandlingOptions } from './turn_handling.js';
99
import { migrateLegacyOptions } from './utils.js';
1010

1111
beforeAll(() => {
@@ -17,9 +17,9 @@ describe('migrateLegacyOptions', () => {
1717
const result = migrateLegacyOptions({});
1818

1919
expect(result.options.turnHandling).toEqual({
20-
turnDetection: defaultTurnHandlingConfig.turnDetection,
21-
endpointing: defaultEndpointingConfig,
22-
interruption: defaultInterruptionConfig,
20+
turnDetection: defaultTurnHandlingOptions.turnDetection,
21+
endpointing: defaultEndpointingOptions,
22+
interruption: defaultInterruptionOptions,
2323
});
2424
expect(result.options.maxToolSteps).toBe(3);
2525
expect(result.options.preemptiveGeneration).toBe(false);
@@ -44,14 +44,14 @@ describe('migrateLegacyOptions', () => {
4444
expect(result.options.turnHandling.endpointing!.maxDelay).toBe(5000);
4545
});
4646

47-
it('should set interruption.mode to false when allowInterruptions is false', () => {
47+
it('should set interruption.enabled to false when allowInterruptions is false', () => {
4848
const result = migrateLegacyOptions({
4949
options: {
5050
allowInterruptions: false,
5151
},
5252
});
5353

54-
expect(result.options.turnHandling.interruption!.mode).toBe(false);
54+
expect(result.options.turnHandling.interruption!.enabled).toBe(false);
5555
});
5656

5757
it('should give options precedence over voiceOptions when both are provided', () => {

agents/src/voice/turn_config/utils.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {
77
type InternalSessionOptions,
88
defaultSessionOptions,
99
} from '../agent_session.js';
10-
import { defaultEndpointingConfig } from './endpointing.js';
11-
import { defaultInterruptionConfig } from './interruption.js';
12-
import { type TurnHandlingConfig, defaultTurnHandlingConfig } from './turn_handling.js';
10+
import { defaultEndpointingOptions } from './endpointing.js';
11+
import { defaultInterruptionOptions } from './interruption.js';
12+
import { type TurnHandlingOptions, defaultTurnHandlingOptions } from './turn_handling.js';
1313

1414
export function migrateLegacyOptions<UserData>(
1515
legacyOptions: AgentSessionOptions<UserData>,
@@ -50,7 +50,7 @@ export function migrateLegacyOptions<UserData>(
5050

5151
const mergedOptions = structuredClone({ ...cloneableVoiceOptions, ...cloneableSessionOptions });
5252

53-
const turnHandling: TurnHandlingConfig = {
53+
const turnHandling: TurnHandlingOptions = {
5454
interruption: {
5555
discardAudioIfUninterruptible: mergedOptions?.discardAudioIfUninterruptible,
5656
minDuration: mergedOptions?.minInterruptionDuration,
@@ -68,7 +68,7 @@ export function migrateLegacyOptions<UserData>(
6868
} as const;
6969

7070
if (mergedOptions?.allowInterruptions === false) {
71-
turnHandling.interruption.mode = false;
71+
turnHandling.interruption.enabled = false;
7272
}
7373

7474
const optionsWithDefaults = {
@@ -94,10 +94,10 @@ export function stripUndefined<T extends object>(obj: T): Partial<T> {
9494
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as Partial<T>;
9595
}
9696

97-
export function mergeWithDefaults(config: TurnHandlingConfig) {
97+
export function mergeWithDefaults(config: TurnHandlingOptions) {
9898
return {
99-
turnDetection: config.turnDetection ?? defaultTurnHandlingConfig.turnDetection,
100-
endpointing: { ...defaultEndpointingConfig, ...stripUndefined(config.endpointing) },
101-
interruption: { ...defaultInterruptionConfig, ...stripUndefined(config.interruption) },
99+
turnDetection: config.turnDetection ?? defaultTurnHandlingOptions.turnDetection,
100+
endpointing: { ...defaultEndpointingOptions, ...stripUndefined(config.endpointing) },
101+
interruption: { ...defaultInterruptionOptions, ...stripUndefined(config.interruption) },
102102
} as const;
103103
}

plugins/livekit/src/turn_detector/base.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ export abstract class EOUModel {
170170

171171
#logger = log();
172172

173+
get model(): string {
174+
return MODEL_REVISIONS[this.modelType];
175+
}
176+
177+
get provider(): string {
178+
return 'livekit';
179+
}
180+
173181
constructor(opts: EOUModelOptions) {
174182
const {
175183
modelType = 'en',

0 commit comments

Comments
 (0)