Skip to content

Commit 988cdac

Browse files
committed
FIX: update settings
1 parent c96cf31 commit 988cdac

12 files changed

+433
-229
lines changed

packages/lib/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@palabra-ai/translator",
3-
"version": "0.0.5",
3+
"version": "0.0.6",
44
"private": false,
55
"main": "dist/lib.js",
66
"types": "dist/index.d.ts",
@@ -20,7 +20,8 @@
2020
},
2121
"dependencies": {
2222
"livekit-client": "2.13.0",
23-
"typed-emitter": "^2.1.0"
23+
"typed-emitter": "^2.1.0",
24+
"ts-deepmerge": "^7.0.3"
2425
},
2526
"devDependencies": {
2627
"@eslint/js": "^9.28.0",

packages/lib/src/PalabraClient.model.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SourceLangCode } from '~/utils/source';
22
import { TargetLangCode } from '~/utils/target';
3+
import { PipelineConfigManager } from './config/PipelineConfigManager';
34

45
export interface ClientCredentialsAuth {
56
clientId: string;
@@ -10,16 +11,19 @@ export interface UserTokenAuth {
1011
userToken: string;
1112
}
1213

13-
export interface PalabraClientData {
14+
15+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
16+
export interface PalabraClientData<CM extends PipelineConfigManager<any> = PipelineConfigManager<any>> {
1417
auth:ClientCredentialsAuth | UserTokenAuth;
1518
translateFrom: SourceLangCode;
1619
translateTo: TargetLangCode;
1720
handleOriginalTrack: () => Promise<MediaStreamTrack>;
18-
transportType?: 'webrtc'; // TODO: add websocket transport | 'websocket'
21+
transportType?: 'webrtc';
1922
apiBaseUrl?: string;
2023
intent?:string;
2124
audioContext?:AudioContext;
2225
ignoreAudioContext?:boolean;
26+
configManager?: CM;
2327
}
2428

2529
export type TrackSid = string;

packages/lib/src/PalabraClient.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import { supportsAudioContextSetSinkId, VolumeNode } from './utils';
2020
import { ConnectionState } from 'livekit-client';
2121
import { PipelineConfig } from './config';
2222

23-
export class PalabraClient extends PalabraBaseEventEmitter {
23+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
24+
export class PalabraClient<CM extends PipelineConfigManager<any> = PipelineConfigManager<unknown>> extends PalabraBaseEventEmitter {
2425
private translateFrom: SourceLangCode;
2526
private translateTo: TargetLangCode;
2627
private auth: PalabraClientData['auth'];
@@ -30,7 +31,7 @@ export class PalabraClient extends PalabraBaseEventEmitter {
3031
private originalTrackVolumeNode: VolumeNode | null = null;
3132
public transport: PalabraWebRtcTransport | null = null;
3233
private transportType: PalabraClientData['transportType'];
33-
private configManager: PipelineConfigManager;
34+
private configManager: CM;
3435
private audioContext: AudioContext;
3536

3637

@@ -50,7 +51,7 @@ export class PalabraClient extends PalabraBaseEventEmitter {
5051

5152
private ignoreAudioContext: PalabraClientData['ignoreAudioContext'];
5253

53-
constructor(data: PalabraClientData) {
54+
constructor(data: PalabraClientData<CM>) {
5455
super();
5556

5657
this.auth = data.auth;
@@ -61,20 +62,21 @@ export class PalabraClient extends PalabraBaseEventEmitter {
6162

6263
this.transportType = data.transportType ?? 'webrtc';
6364

64-
this.initConfig();
65-
6665
this.shouldPlayTranslation = false;
6766

6867
this.ignoreAudioContext = data.ignoreAudioContext ?? false;
6968

70-
if (data.audioContext) {
71-
this.audioContext = data.audioContext;
69+
if (data.configManager) {
70+
this.configManager = data.configManager;
7271
}
72+
73+
this.initConfig();
74+
75+
this.initAudioContext(data.audioContext);
7376
}
7477

7578
public async startTranslation(): Promise<boolean> {
7679
try {
77-
this.initAudioContext();
7880
await this.wrapOriginalTrack();
7981
const transport = await this.createSession();
8082
this.initTransportHandlers();
@@ -93,7 +95,6 @@ export class PalabraClient extends PalabraBaseEventEmitter {
9395
await this.deleteSession();
9496
this.transport = null;
9597
this.stopPlayback();
96-
this.closeAudioContext();
9798
this.cleanUnusedTracks([]);
9899
this.translationStatus = 'stopped';
99100
this.cleanupOriginalTrack();
@@ -247,7 +248,7 @@ export class PalabraClient extends PalabraBaseEventEmitter {
247248
return this.transport;
248249
}
249250

250-
public getConfigManager() {
251+
public getConfigManager(): CM {
251252
return this.configManager;
252253
}
253254

@@ -317,6 +318,7 @@ export class PalabraClient extends PalabraBaseEventEmitter {
317318

318319
public async cleanup() {
319320
await this.stopTranslation();
321+
this.closeAudioContext();
320322
this.initConfig();
321323
}
322324

@@ -337,9 +339,9 @@ export class PalabraClient extends PalabraBaseEventEmitter {
337339
});
338340
}
339341

340-
private async initAudioContext() {
342+
private async initAudioContext(audioContext?: AudioContext) {
341343
if (this.audioContext || this.ignoreAudioContext) return;
342-
this.audioContext = new AudioContext();
344+
this.audioContext = audioContext ?? new AudioContext();
343345
}
344346

345347
private closeAudioContext() {
@@ -348,7 +350,9 @@ export class PalabraClient extends PalabraBaseEventEmitter {
348350
}
349351

350352
private initConfig() {
351-
this.configManager = new PipelineConfigManager(this.transportType);
353+
if (!this.configManager) {
354+
this.configManager = new PipelineConfigManager() as CM;
355+
}
352356

353357
this.configManager.setSourceLanguage(this.translateFrom as SourceLangCode);
354358
this.configManager.addTranslationTarget({ target_language: this.translateTo as TargetLangCode });

packages/lib/src/__tests__/PalabraClient.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PalabraClient } from '../PalabraClient';
33
import type { TargetLangCode } from '../utils/target';
44
import type { SourceLangCode } from '../utils/source';
55
import { EVENT_START_TRANSLATION, EVENT_STOP_TRANSLATION } from '../transport/PalabraWebRtcTransport.model';
6+
import { PipelineConfigManager } from '~/config';
67

78
// Mock MediaStreamTrack for tests
89
class MockMediaStreamTrack {
@@ -140,6 +141,88 @@ describe('PalabraClient', () => {
140141
client = new PalabraClient(baseConstructorData);
141142
});
142143

144+
describe('ConfigManager', () => {
145+
it('should set value and get value with extensions', ()=>{
146+
const manager = new PipelineConfigManager({ initialExtension: { testProp: 12 } });
147+
148+
const cl = new PalabraClient({ ...baseConstructorData, configManager: manager });
149+
expect((cl.getConfigManager()).getValue('testProp')).toBe(12);
150+
151+
(cl.getConfigManager()).setValue('testProp', 13);
152+
expect((cl.getConfigManager()).getValue('testProp')).toBe(13);
153+
154+
manager.setValue('testProp', 14);
155+
expect((cl.getConfigManager()).getValue('testProp')).toBe(14);
156+
});
157+
158+
it('should set value and get value without extensions', ()=>{
159+
const cl = new PalabraClient({ ...baseConstructorData });
160+
expect((cl.getConfigManager()).getValue('preprocessing.enable_vad')).toBe(true);
161+
});
162+
});
163+
164+
describe('AudioContext', () => {
165+
it('should not close AudioContext in stopTranslation', async () => {
166+
const closeAudioContextSpy = vi.spyOn(client as unknown as { closeAudioContext: () => void }, 'closeAudioContext').mockImplementation(() => undefined);
167+
await client.stopTranslation();
168+
expect(closeAudioContextSpy).not.toHaveBeenCalled();
169+
});
170+
171+
it('should not call close method on AudioContext in stopTranslation', async() => {
172+
const ctx = new AudioContext();
173+
const closeAudioContextSpy = vi.spyOn(ctx, 'close').mockImplementation(() => undefined);
174+
const localClient = new PalabraClient({ ...baseConstructorData, audioContext: ctx });
175+
await localClient.stopTranslation();
176+
expect(closeAudioContextSpy).not.toHaveBeenCalled();
177+
});
178+
179+
it('should close AudioContext in cleanup', async () => {
180+
const closeAudioContextSpy = vi.spyOn(client as unknown as { closeAudioContext: () => void }, 'closeAudioContext').mockImplementation(() => undefined);
181+
await client.cleanup();
182+
expect(closeAudioContextSpy).toHaveBeenCalled();
183+
});
184+
185+
it('should ignore AudioContext creation when ignoreAudioContext is true and audioContext is provided', async() => {
186+
const ctx = new AudioContext();
187+
const localClient = new PalabraClient({ ...baseConstructorData, audioContext: ctx, ignoreAudioContext: true });
188+
// @ts-expect-error: audioContext is private
189+
expect(localClient.audioContext).toBeUndefined();
190+
});
191+
192+
it('should create a new AudioContext when ignoreAudioContext is false and audioContext is not provided', async() => {
193+
const localClient = new PalabraClient({ ...baseConstructorData, ignoreAudioContext: false });
194+
// @ts-expect-error: audioContext is private
195+
expect(localClient.audioContext).toBeDefined();
196+
});
197+
198+
it('should use provided AudioContext', async() => {
199+
const ctx = new AudioContext();
200+
// @ts-expect-error field for test
201+
ctx.field = 'test';
202+
203+
const localClient = new PalabraClient({ ...baseConstructorData, audioContext: ctx, ignoreAudioContext: false });
204+
// @ts-expect-error: audioContext is private
205+
expect(localClient.audioContext).toBeDefined();
206+
// @ts-expect-error: field for test
207+
expect(localClient.audioContext.field).toBe('test');
208+
});
209+
210+
it('should create a new AudioContext when audioContext is not provided', async() => {
211+
const localClient = new PalabraClient(baseConstructorData);
212+
await localClient.startTranslation();
213+
// @ts-expect-error: audioContext is private
214+
expect(localClient.audioContext).toBeDefined();
215+
});
216+
});
217+
218+
it('should get api client', () => {
219+
expect(client.getApiClient()).toBeDefined();
220+
});
221+
222+
it('should get config manager', () => {
223+
expect(client.getConfigManager()).toBeDefined();
224+
});
225+
143226
it('should create a new PalabraClient', () => {
144227
expect(client).toBeDefined();
145228
});

packages/lib/src/config/PipelineConfig.model.ts

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface StreamConfigBase {
66
}
77

88
export interface StreamConfigWebRtc extends StreamConfigBase {
9+
content_type: 'audio';
910
source?: {
1011
type: 'webrtc';
1112
};
@@ -43,55 +44,18 @@ export interface PreprocessingConfig {
4344

4445
export interface SentenceSplitterConfig {
4546
enabled: boolean;
46-
splitter_model: 'auto' | string;
47-
advanced: {
48-
min_sentence_characters: number;
49-
min_sentence_seconds: number;
50-
min_split_interval: number;
51-
context_size: number;
52-
segments_after_restart: number;
53-
step_size: number;
54-
max_steps_without_eos: number;
55-
force_end_of_segment: number;
56-
};
5747
}
5848

5949
export interface VerificationConfig {
60-
verification_model: 'auto' | string;
61-
allow_verification_glossaries: boolean;
6250
auto_transcription_correction: boolean;
6351
transcription_correction_style: string | null;
6452
}
65-
66-
export interface TranscriptionAdvancedConfig {
67-
filler_phrases: {
68-
enabled: boolean;
69-
min_transcription_len: number;
70-
min_transcription_time: number;
71-
phrase_chance: number;
72-
};
73-
ignore_languages: SourceLangCode[];
74-
}
75-
7653
export interface TranscriptionConfig {
7754
source_language: SourceLangCode;
7855
detectable_languages: SourceLangCode[];
79-
asr_model: 'auto' | string;
80-
denoise: 'none' | string;
81-
allow_hotwords_glossaries: boolean;
82-
supress_numeral_tokens: boolean;
83-
diarize_speakers: boolean;
84-
priority: 'normal' | string;
85-
min_alignment_score: number;
86-
max_alignment_cer: number;
8756
segment_confirmation_silence_threshold: number;
88-
only_confirm_by_silence: boolean;
89-
batched_inference: boolean;
90-
force_detect_language: boolean;
91-
calculate_voice_loudness: boolean;
9257
sentence_splitter: SentenceSplitterConfig;
9358
verification: VerificationConfig;
94-
advanced: TranscriptionAdvancedConfig;
9559
}
9660

9761
export type AddTranslationArgs = Partial<Omit<TranslationConfig, 'target_language'>> & Pick<TranslationConfig, 'target_language'>;
@@ -102,33 +66,15 @@ export interface VoiceTimbreDetectionConfig {
10266
low_timbre_voices: string[];
10367
}
10468

105-
export interface SpeechGenerationAdvancedConfig {
106-
f0_variance_factor: number;
107-
energy_variance_factor: number;
108-
with_custom_stress: boolean;
109-
}
110-
11169
export interface SpeechGenerationConfig {
112-
tts_model: 'auto' | string;
11370
voice_cloning: boolean;
114-
voice_cloning_mode: 'static_10' | string;
115-
denoise_voice_samples: boolean;
11671
voice_id: string;
11772
voice_timbre_detection: VoiceTimbreDetectionConfig;
118-
speech_tempo_auto: boolean;
119-
speech_tempo_timings_factor: number;
120-
speech_tempo_adjustment_factor: number;
121-
advanced: SpeechGenerationAdvancedConfig;
12273
}
12374

12475
export interface TranslationConfig {
12576
target_language: TargetLangCode;
126-
allowed_source_languages: SourceLangCode[];
127-
translation_model: 'auto' | 'alpha' | string;
128-
allow_translation_glossaries: boolean;
129-
style: string | null;
13077
translate_partial_transcriptions: boolean;
131-
advanced: Record<string, unknown>;
13278
speech_generation: SpeechGenerationConfig;
13379
}
13480

@@ -146,8 +92,7 @@ export type AllowedMessageTypes = (string
14692
| 'translated_transcription'
14793
| 'partial_translated_transcription'
14894
| 'partial_transcription'
149-
| 'validated_transcription'
150-
| 'pipeline_timings');
95+
| 'validated_transcription');
15196

15297
export interface PipelineConfig {
15398
input_stream: StreamConfig;

0 commit comments

Comments
 (0)