@livekit/agents 1.1.0-dev.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +2 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/constants.cjs +3 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/cpu.cjs +189 -0
- package/dist/cpu.cjs.map +1 -0
- package/dist/cpu.d.cts +24 -0
- package/dist/cpu.d.ts +24 -0
- package/dist/cpu.d.ts.map +1 -0
- package/dist/cpu.js +152 -0
- package/dist/cpu.js.map +1 -0
- package/dist/cpu.test.cjs +227 -0
- package/dist/cpu.test.cjs.map +1 -0
- package/dist/cpu.test.js +204 -0
- package/dist/cpu.test.js.map +1 -0
- package/dist/index.cjs +12 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/inference/interruption/defaults.cjs +1 -1
- package/dist/inference/interruption/defaults.cjs.map +1 -1
- package/dist/inference/interruption/defaults.d.cts +1 -1
- package/dist/inference/interruption/defaults.d.ts +1 -1
- package/dist/inference/interruption/defaults.d.ts.map +1 -1
- package/dist/inference/interruption/defaults.js +1 -1
- package/dist/inference/interruption/defaults.js.map +1 -1
- package/dist/inference/interruption/http_transport.cjs +44 -28
- package/dist/inference/interruption/http_transport.cjs.map +1 -1
- package/dist/inference/interruption/http_transport.d.ts.map +1 -1
- package/dist/inference/interruption/http_transport.js +45 -29
- package/dist/inference/interruption/http_transport.js.map +1 -1
- package/dist/inference/interruption/interruption_detector.cjs +22 -5
- package/dist/inference/interruption/interruption_detector.cjs.map +1 -1
- package/dist/inference/interruption/interruption_detector.d.cts +2 -2
- package/dist/inference/interruption/interruption_detector.d.ts +2 -2
- package/dist/inference/interruption/interruption_detector.d.ts.map +1 -1
- package/dist/inference/interruption/interruption_detector.js +22 -5
- package/dist/inference/interruption/interruption_detector.js.map +1 -1
- package/dist/inference/interruption/interruption_stream.cjs +4 -4
- package/dist/inference/interruption/interruption_stream.cjs.map +1 -1
- package/dist/inference/interruption/interruption_stream.js +4 -4
- package/dist/inference/interruption/interruption_stream.js.map +1 -1
- package/dist/inference/interruption/types.cjs.map +1 -1
- package/dist/inference/interruption/types.d.cts +2 -2
- package/dist/inference/interruption/types.d.ts +2 -2
- package/dist/inference/interruption/types.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.cjs +60 -47
- package/dist/inference/interruption/ws_transport.cjs.map +1 -1
- package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.js +60 -47
- package/dist/inference/interruption/ws_transport.js.map +1 -1
- package/dist/inference/llm.cjs.map +1 -1
- package/dist/inference/llm.d.cts +1 -1
- package/dist/inference/llm.d.ts +1 -1
- package/dist/inference/llm.d.ts.map +1 -1
- package/dist/inference/llm.js.map +1 -1
- package/dist/inference/stt.cjs +20 -12
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.cts +3 -2
- package/dist/inference/stt.d.ts +3 -2
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +20 -12
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/stt.test.cjs +14 -0
- package/dist/inference/stt.test.cjs.map +1 -1
- package/dist/inference/stt.test.js +14 -0
- package/dist/inference/stt.test.js.map +1 -1
- package/dist/inference/tts.cjs +13 -4
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +8 -1
- package/dist/inference/tts.d.ts +8 -1
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +13 -4
- package/dist/inference/tts.js.map +1 -1
- package/dist/inference/tts.test.cjs +10 -0
- package/dist/inference/tts.test.cjs.map +1 -1
- package/dist/inference/tts.test.js +10 -0
- package/dist/inference/tts.test.js.map +1 -1
- package/dist/ipc/job_proc_lazy_main.cjs +41 -23
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
- package/dist/ipc/job_proc_lazy_main.js +41 -23
- package/dist/ipc/job_proc_lazy_main.js.map +1 -1
- package/dist/job.cjs +1 -1
- package/dist/job.cjs.map +1 -1
- package/dist/job.js +1 -1
- package/dist/job.js.map +1 -1
- package/dist/language.cjs +394 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +15 -0
- package/dist/language.d.ts +15 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +363 -0
- package/dist/language.js.map +1 -0
- package/dist/language.test.cjs +43 -0
- package/dist/language.test.cjs.map +1 -0
- package/dist/language.test.js +49 -0
- package/dist/language.test.js.map +1 -0
- package/dist/llm/index.cjs +2 -0
- package/dist/llm/index.cjs.map +1 -1
- package/dist/llm/index.d.cts +1 -1
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +2 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/stream/deferred_stream.cjs +6 -2
- package/dist/stream/deferred_stream.cjs.map +1 -1
- package/dist/stream/deferred_stream.d.ts.map +1 -1
- package/dist/stream/deferred_stream.js +6 -2
- package/dist/stream/deferred_stream.js.map +1 -1
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +2 -1
- package/dist/stt/stt.d.ts +2 -1
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js.map +1 -1
- package/dist/utils.cjs +15 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +8 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +13 -0
- package/dist/utils.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/agent.cjs +14 -17
- package/dist/voice/agent.cjs.map +1 -1
- package/dist/voice/agent.d.cts +10 -11
- package/dist/voice/agent.d.ts +10 -11
- package/dist/voice/agent.d.ts.map +1 -1
- package/dist/voice/agent.js +15 -18
- package/dist/voice/agent.js.map +1 -1
- package/dist/voice/agent.test.cjs +194 -0
- package/dist/voice/agent.test.cjs.map +1 -1
- package/dist/voice/agent.test.js +195 -1
- package/dist/voice/agent.test.js.map +1 -1
- package/dist/voice/agent_activity.cjs +116 -39
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +2 -0
- package/dist/voice/agent_activity.d.ts +2 -0
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +117 -40
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_activity.test.cjs +135 -0
- package/dist/voice/agent_activity.test.cjs.map +1 -0
- package/dist/voice/agent_activity.test.js +134 -0
- package/dist/voice/agent_activity.test.js.map +1 -0
- package/dist/voice/agent_session.cjs +38 -38
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +65 -56
- package/dist/voice/agent_session.d.ts +65 -56
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +37 -37
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +106 -52
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +4 -2
- package/dist/voice/audio_recognition.d.ts +4 -2
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +106 -52
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/audio_recognition_span.test.cjs +84 -22
- package/dist/voice/audio_recognition_span.test.cjs.map +1 -1
- package/dist/voice/audio_recognition_span.test.js +90 -23
- package/dist/voice/audio_recognition_span.test.js.map +1 -1
- package/dist/voice/events.cjs +1 -1
- package/dist/voice/events.cjs.map +1 -1
- package/dist/voice/events.d.cts +4 -3
- package/dist/voice/events.d.ts +4 -3
- package/dist/voice/events.d.ts.map +1 -1
- package/dist/voice/events.js +1 -1
- package/dist/voice/events.js.map +1 -1
- package/dist/voice/index.cjs +9 -1
- package/dist/voice/index.cjs.map +1 -1
- package/dist/voice/index.d.cts +1 -1
- package/dist/voice/index.d.ts +1 -1
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +10 -1
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/remote_session.cjs +922 -0
- package/dist/voice/remote_session.cjs.map +1 -0
- package/dist/voice/remote_session.d.cts +108 -0
- package/dist/voice/remote_session.d.ts +108 -0
- package/dist/voice/remote_session.d.ts.map +1 -0
- package/dist/voice/remote_session.js +887 -0
- package/dist/voice/remote_session.js.map +1 -0
- package/dist/voice/report.cjs +11 -10
- package/dist/voice/report.cjs.map +1 -1
- package/dist/voice/report.d.cts +5 -3
- package/dist/voice/report.d.ts +5 -3
- package/dist/voice/report.d.ts.map +1 -1
- package/dist/voice/report.js +11 -10
- package/dist/voice/report.js.map +1 -1
- package/dist/voice/report.test.cjs +15 -0
- package/dist/voice/report.test.cjs.map +1 -1
- package/dist/voice/report.test.js +15 -0
- package/dist/voice/report.test.js.map +1 -1
- package/dist/voice/room_io/room_io.cjs +39 -0
- package/dist/voice/room_io/room_io.cjs.map +1 -1
- package/dist/voice/room_io/room_io.d.cts +3 -1
- package/dist/voice/room_io/room_io.d.ts +3 -1
- package/dist/voice/room_io/room_io.d.ts.map +1 -1
- package/dist/voice/room_io/room_io.js +40 -1
- package/dist/voice/room_io/room_io.js.map +1 -1
- package/dist/voice/turn_config/interruption.cjs.map +1 -1
- package/dist/voice/turn_config/interruption.d.cts +1 -1
- package/dist/voice/turn_config/interruption.d.ts +1 -1
- package/dist/voice/turn_config/interruption.d.ts.map +1 -1
- package/dist/voice/turn_config/interruption.js.map +1 -1
- package/dist/voice/turn_config/utils.cjs +95 -35
- package/dist/voice/turn_config/utils.cjs.map +1 -1
- package/dist/voice/turn_config/utils.d.cts +17 -5
- package/dist/voice/turn_config/utils.d.ts +17 -5
- package/dist/voice/turn_config/utils.d.ts.map +1 -1
- package/dist/voice/turn_config/utils.js +93 -35
- package/dist/voice/turn_config/utils.js.map +1 -1
- package/dist/voice/turn_config/utils.test.cjs +83 -41
- package/dist/voice/turn_config/utils.test.cjs.map +1 -1
- package/dist/voice/turn_config/utils.test.js +84 -42
- package/dist/voice/turn_config/utils.test.js.map +1 -1
- package/dist/worker.cjs +6 -29
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +6 -19
- package/dist/worker.js.map +1 -1
- package/package.json +3 -2
- package/src/cli.ts +2 -0
- package/src/constants.ts +1 -0
- package/src/cpu.test.ts +239 -0
- package/src/cpu.ts +173 -0
- package/src/index.ts +13 -15
- package/src/inference/interruption/defaults.ts +1 -1
- package/src/inference/interruption/http_transport.ts +49 -30
- package/src/inference/interruption/interruption_detector.ts +22 -6
- package/src/inference/interruption/interruption_stream.ts +4 -4
- package/src/inference/interruption/types.ts +2 -2
- package/src/inference/interruption/ws_transport.ts +63 -59
- package/src/inference/llm.ts +3 -1
- package/src/inference/stt.test.ts +17 -0
- package/src/inference/stt.ts +22 -14
- package/src/inference/tts.test.ts +12 -0
- package/src/inference/tts.ts +22 -6
- package/src/ipc/job_proc_lazy_main.ts +44 -24
- package/src/job.ts +1 -1
- package/src/language.test.ts +62 -0
- package/src/language.ts +380 -0
- package/src/llm/index.ts +2 -0
- package/src/stream/deferred_stream.ts +5 -1
- package/src/stt/stt.ts +2 -1
- package/src/utils.ts +20 -0
- package/src/voice/agent.test.ts +208 -1
- package/src/voice/agent.ts +21 -22
- package/src/voice/agent_activity.test.ts +194 -0
- package/src/voice/agent_activity.ts +161 -43
- package/src/voice/agent_session.ts +103 -92
- package/src/voice/audio_recognition.ts +124 -61
- package/src/voice/audio_recognition_span.test.ts +115 -35
- package/src/voice/events.ts +4 -3
- package/src/voice/index.ts +10 -1
- package/src/voice/remote_session.ts +1083 -0
- package/src/voice/report.test.ts +22 -3
- package/src/voice/report.ts +31 -14
- package/src/voice/room_io/room_io.ts +52 -2
- package/src/voice/turn_config/interruption.ts +1 -1
- package/src/voice/turn_config/utils.test.ts +91 -43
- package/src/voice/turn_config/utils.ts +120 -56
- package/src/worker.ts +34 -50
- package/dist/voice/client_events.cjs +0 -554
- package/dist/voice/client_events.cjs.map +0 -1
- package/dist/voice/client_events.d.cts +0 -195
- package/dist/voice/client_events.d.ts +0 -195
- package/dist/voice/client_events.d.ts.map +0 -1
- package/dist/voice/client_events.js +0 -548
- package/dist/voice/client_events.js.map +0 -1
- package/dist/voice/wire_format.cjs +0 -798
- package/dist/voice/wire_format.cjs.map +0 -1
- package/dist/voice/wire_format.d.cts +0 -5503
- package/dist/voice/wire_format.d.ts +0 -5503
- package/dist/voice/wire_format.d.ts.map +0 -1
- package/dist/voice/wire_format.js +0 -728
- package/dist/voice/wire_format.js.map +0 -1
- package/src/voice/client_events.ts +0 -838
- package/src/voice/wire_format.ts +0 -827
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/inference/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { beforeAll, describe, expect, it } from 'vitest';\nimport { initializeLogger } from '../log.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { TTS, type TTSFallbackModel, normalizeTTSFallback, parseTTSModelString } from './tts.js';\n\nbeforeAll(() => {\n initializeLogger({ level: 'silent', pretty: false });\n});\n\n/** Helper to create TTS with required credentials. */\nfunction makeTts(overrides: Record<string, unknown> = {}) {\n const defaults = {\n model: 'cartesia/sonic' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n };\n return new TTS({ ...defaults, ...overrides });\n}\n\ndescribe('parseTTSModelString', () => {\n it('simple model without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia');\n expect(model).toBe('cartesia');\n expect(voice).toBeUndefined();\n });\n\n it('model with voice suffix', () => {\n const [model, voice] = parseTTSModelString('cartesia:my-voice-id');\n expect(model).toBe('cartesia');\n expect(voice).toBe('my-voice-id');\n });\n\n it('provider/model format without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBeUndefined();\n });\n\n it('provider/model format with voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:my-voice-id');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('my-voice-id');\n });\n\n it.each([\n ['elevenlabs/eleven_flash_v2:voice123', 'elevenlabs/eleven_flash_v2', 'voice123'],\n ['rime:speaker-a', 'rime', 'speaker-a'],\n ['rime/mist:narrator', 'rime/mist', 'narrator'],\n ['inworld/inworld-tts-1:character', 'inworld/inworld-tts-1', 'character'],\n ['cartesia/sonic-turbo:deep-voice', 'cartesia/sonic-turbo', 'deep-voice'],\n ])('various providers and voices: %s', (modelStr, expectedModel, expectedVoice) => {\n const [model, voice] = parseTTSModelString(modelStr);\n expect(model).toBe(expectedModel);\n expect(voice).toBe(expectedVoice);\n });\n\n it('empty voice after colon', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('');\n });\n});\n\ndescribe('normalizeTTSFallback', () => {\n it('single string model', () => {\n const result = normalizeTTSFallback('cartesia/sonic');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n\n it('single string model with voice', () => {\n const result = normalizeTTSFallback('cartesia/sonic:my-voice');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('single FallbackModel dict', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: 'narrator' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('list of string models', () => {\n const result = normalizeTTSFallback(['cartesia/sonic', 'elevenlabs/eleven_flash_v2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs/eleven_flash_v2', voice: '' },\n ]);\n });\n\n it('list of string models with voices', () => {\n const result = normalizeTTSFallback(['cartesia/sonic:voice1', 'elevenlabs:voice2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'voice2' },\n ]);\n });\n\n it('list of FallbackModel dicts', () => {\n const fallbacks: TTSFallbackModel[] = [\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ];\n const result = normalizeTTSFallback(fallbacks);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('mixed list of strings and dicts', () => {\n const result = normalizeTTSFallback([\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' } as TTSFallbackModel,\n 'rime/mist',\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('FallbackModel with extraKwargs is preserved', () => {\n const fallback: TTSFallbackModel = {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('list with extraKwargs preserved', () => {\n const result = normalizeTTSFallback([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } } as TTSFallbackModel,\n 'elevenlabs:voice2',\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } } as TTSFallbackModel,\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } },\n { model: 'elevenlabs', voice: 'voice2' },\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } },\n ]);\n });\n\n it('empty list returns empty list', () => {\n const result = normalizeTTSFallback([]);\n expect(result).toEqual([]);\n });\n\n it('FallbackModel with empty voice', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: '' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n});\n\ndescribe('TTS constructor fallback and connOptions', () => {\n it('fallback not given defaults to undefined', () => {\n const tts = makeTts();\n expect(tts['opts'].fallback).toBeUndefined();\n });\n\n it('fallback single string is normalized', () => {\n const tts = makeTts({ fallback: 'elevenlabs/eleven_flash_v2' });\n expect(tts['opts'].fallback).toEqual([{ model: 'elevenlabs/eleven_flash_v2', voice: '' }]);\n });\n\n it('fallback single string with voice is normalized', () => {\n const tts = makeTts({ fallback: 'cartesia/sonic:my-voice' });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('fallback list of strings is normalized', () => {\n const tts = makeTts({ fallback: ['cartesia/sonic', 'elevenlabs'] });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('fallback single FallbackModel is normalized to list', () => {\n const tts = makeTts({ fallback: { model: 'cartesia/sonic', voice: 'narrator' } });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('fallback with extraKwargs is preserved', () => {\n const tts = makeTts({\n fallback: {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n });\n expect(tts['opts'].fallback).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('fallback mixed list is normalized', () => {\n const tts = makeTts({\n fallback: [\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n 'rime/mist',\n ],\n });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('connOptions not given uses default', () => {\n const tts = makeTts();\n expect(tts['opts'].connOptions).toEqual(DEFAULT_API_CONNECT_OPTIONS);\n });\n\n it('connOptions custom timeout', () => {\n const custom: APIConnectOptions = { timeoutMs: 30000, maxRetry: 3, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(30000);\n });\n\n it('connOptions custom maxRetry', () => {\n const custom: APIConnectOptions = { timeoutMs: 10000, maxRetry: 5, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.maxRetry).toBe(5);\n });\n\n it('connOptions full custom', () => {\n const custom: APIConnectOptions = { timeoutMs: 60000, maxRetry: 10, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(60000);\n expect(tts['opts'].connOptions!.maxRetry).toBe(10);\n expect(tts['opts'].connOptions!.retryIntervalMs).toBe(2000);\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/inference/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { beforeAll, describe, expect, it } from 'vitest';\nimport { normalizeLanguage } from '../language.js';\nimport { initializeLogger } from '../log.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { TTS, type TTSFallbackModel, normalizeTTSFallback, parseTTSModelString } from './tts.js';\n\nbeforeAll(() => {\n initializeLogger({ level: 'silent', pretty: false });\n});\n\n/** Helper to create TTS with required credentials. */\nfunction makeTts(overrides: Record<string, unknown> = {}) {\n const defaults = {\n model: 'cartesia/sonic' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n };\n return new TTS({ ...defaults, ...overrides });\n}\n\ndescribe('parseTTSModelString', () => {\n it('simple model without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia');\n expect(model).toBe('cartesia');\n expect(voice).toBeUndefined();\n });\n\n it('model with voice suffix', () => {\n const [model, voice] = parseTTSModelString('cartesia:my-voice-id');\n expect(model).toBe('cartesia');\n expect(voice).toBe('my-voice-id');\n });\n\n it('provider/model format without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBeUndefined();\n });\n\n it('provider/model format with voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:my-voice-id');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('my-voice-id');\n });\n\n it.each([\n ['elevenlabs/eleven_flash_v2:voice123', 'elevenlabs/eleven_flash_v2', 'voice123'],\n ['rime:speaker-a', 'rime', 'speaker-a'],\n ['rime/mist:narrator', 'rime/mist', 'narrator'],\n ['inworld/inworld-tts-1:character', 'inworld/inworld-tts-1', 'character'],\n ['cartesia/sonic-turbo:deep-voice', 'cartesia/sonic-turbo', 'deep-voice'],\n ])('various providers and voices: %s', (modelStr, expectedModel, expectedVoice) => {\n const [model, voice] = parseTTSModelString(modelStr);\n expect(model).toBe(expectedModel);\n expect(voice).toBe(expectedVoice);\n });\n\n it('empty voice after colon', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('');\n });\n});\n\ndescribe('normalizeTTSFallback', () => {\n it('single string model', () => {\n const result = normalizeTTSFallback('cartesia/sonic');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n\n it('single string model with voice', () => {\n const result = normalizeTTSFallback('cartesia/sonic:my-voice');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('single FallbackModel dict', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: 'narrator' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('list of string models', () => {\n const result = normalizeTTSFallback(['cartesia/sonic', 'elevenlabs/eleven_flash_v2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs/eleven_flash_v2', voice: '' },\n ]);\n });\n\n it('list of string models with voices', () => {\n const result = normalizeTTSFallback(['cartesia/sonic:voice1', 'elevenlabs:voice2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'voice2' },\n ]);\n });\n\n it('list of FallbackModel dicts', () => {\n const fallbacks: TTSFallbackModel[] = [\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ];\n const result = normalizeTTSFallback(fallbacks);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('mixed list of strings and dicts', () => {\n const result = normalizeTTSFallback([\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' } as TTSFallbackModel,\n 'rime/mist',\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('FallbackModel with extraKwargs is preserved', () => {\n const fallback: TTSFallbackModel = {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('list with extraKwargs preserved', () => {\n const result = normalizeTTSFallback([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } } as TTSFallbackModel,\n 'elevenlabs:voice2',\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } } as TTSFallbackModel,\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } },\n { model: 'elevenlabs', voice: 'voice2' },\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } },\n ]);\n });\n\n it('empty list returns empty list', () => {\n const result = normalizeTTSFallback([]);\n expect(result).toEqual([]);\n });\n\n it('FallbackModel with empty voice', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: '' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n});\n\ndescribe('TTS constructor fallback and connOptions', () => {\n it('normalizes language in constructor', () => {\n const tts = makeTts({ language: 'english' });\n expect(tts['opts'].language).toBe('en');\n });\n\n it('normalizes updated language values', () => {\n const tts = makeTts();\n tts.updateOptions({ language: 'en_US' });\n expect(tts['opts'].language).toBe(normalizeLanguage('en_US'));\n });\n\n it('fallback not given defaults to undefined', () => {\n const tts = makeTts();\n expect(tts['opts'].fallback).toBeUndefined();\n });\n\n it('fallback single string is normalized', () => {\n const tts = makeTts({ fallback: 'elevenlabs/eleven_flash_v2' });\n expect(tts['opts'].fallback).toEqual([{ model: 'elevenlabs/eleven_flash_v2', voice: '' }]);\n });\n\n it('fallback single string with voice is normalized', () => {\n const tts = makeTts({ fallback: 'cartesia/sonic:my-voice' });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('fallback list of strings is normalized', () => {\n const tts = makeTts({ fallback: ['cartesia/sonic', 'elevenlabs'] });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('fallback single FallbackModel is normalized to list', () => {\n const tts = makeTts({ fallback: { model: 'cartesia/sonic', voice: 'narrator' } });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('fallback with extraKwargs is preserved', () => {\n const tts = makeTts({\n fallback: {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n });\n expect(tts['opts'].fallback).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('fallback mixed list is normalized', () => {\n const tts = makeTts({\n fallback: [\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n 'rime/mist',\n ],\n });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('connOptions not given uses default', () => {\n const tts = makeTts();\n expect(tts['opts'].connOptions).toEqual(DEFAULT_API_CONNECT_OPTIONS);\n });\n\n it('connOptions custom timeout', () => {\n const custom: APIConnectOptions = { timeoutMs: 30000, maxRetry: 3, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(30000);\n });\n\n it('connOptions custom maxRetry', () => {\n const custom: APIConnectOptions = { timeoutMs: 10000, maxRetry: 5, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.maxRetry).toBe(5);\n });\n\n it('connOptions full custom', () => {\n const custom: APIConnectOptions = { timeoutMs: 60000, maxRetry: 10, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(60000);\n expect(tts['opts'].connOptions!.maxRetry).toBe(10);\n expect(tts['opts'].connOptions!.retryIntervalMs).toBe(2000);\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -31,11 +31,20 @@ var import_utils = require("../utils.cjs");
|
|
|
31
31
|
var import_worker = require("../worker.cjs");
|
|
32
32
|
const ORPHANED_TIMEOUT = 15 * 1e3;
|
|
33
33
|
const safeSend = (msg) => {
|
|
34
|
-
|
|
35
|
-
process.send
|
|
36
|
-
|
|
34
|
+
try {
|
|
35
|
+
if (process.connected && process.send) {
|
|
36
|
+
process.send(msg);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof Error && error.message.includes("Channel closed")) {
|
|
42
|
+
(0, import_log.log)().debug({ msgCase: msg.case }, "IPC channel closed, message not sent");
|
|
43
|
+
} else {
|
|
44
|
+
(0, import_log.log)().error({ error, msgCase: msg.case }, "IPC send failed unexpectedly");
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
37
47
|
}
|
|
38
|
-
return false;
|
|
39
48
|
};
|
|
40
49
|
class PendingInference {
|
|
41
50
|
promise = new Promise((resolve) => {
|
|
@@ -101,26 +110,35 @@ const startJob = (proc, func, info, closeEvent, logger, joinFuture) => {
|
|
|
101
110
|
);
|
|
102
111
|
}
|
|
103
112
|
}, 1e4);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
try {
|
|
114
|
+
await (0, import_job.runWithJobContextAsync)(ctx, async () => {
|
|
115
|
+
const { tracer, traceTypes } = await import("../telemetry/index.cjs");
|
|
116
|
+
return tracer.startActiveSpan(
|
|
117
|
+
async (span) => {
|
|
118
|
+
var _a;
|
|
119
|
+
span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);
|
|
120
|
+
span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);
|
|
121
|
+
span.setAttribute(traceTypes.ATTR_ROOM_NAME, ((_a = info.job.room) == null ? void 0 : _a.name) ?? "");
|
|
122
|
+
return func(ctx);
|
|
123
|
+
},
|
|
124
|
+
{ name: "job_entrypoint" }
|
|
125
|
+
);
|
|
126
|
+
}).finally(() => {
|
|
127
|
+
clearTimeout(unconnectedTimeout);
|
|
128
|
+
});
|
|
129
|
+
await (0, import_node_events.once)(closeEvent, "close").then((close) => {
|
|
130
|
+
logger.debug("shutting down");
|
|
131
|
+
shutdown = true;
|
|
132
|
+
safeSend({ case: "exiting", value: { reason: close[1] } });
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
logger.error({ error }, "error in entry function");
|
|
121
136
|
shutdown = true;
|
|
122
|
-
safeSend({
|
|
123
|
-
|
|
137
|
+
safeSend({
|
|
138
|
+
case: "exiting",
|
|
139
|
+
value: { reason: error instanceof Error ? error.message : String(error) }
|
|
140
|
+
});
|
|
141
|
+
}
|
|
124
142
|
if (ctx._primaryAgentSession) {
|
|
125
143
|
await ctx._primaryAgentSession.close();
|
|
126
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ipc/job_proc_lazy_main.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Room, RoomEvent, dispose } from '@livekit/rtc-node';\nimport { EventEmitter, once } from 'node:events';\nimport { pathToFileURL } from 'node:url';\nimport type { Logger } from 'pino';\nimport { type Agent, isAgent } from '../generator.js';\nimport { JobContext, JobProcess, type RunningJobInfo, runWithJobContextAsync } from '../job.js';\nimport { initializeLogger, log } from '../log.js';\nimport { Future, shortuuid } from '../utils.js';\nimport { defaultInitializeProcessFunc } from '../worker.js';\nimport type { InferenceExecutor } from './inference_executor.js';\nimport type { IPCMessage } from './message.js';\n\nconst ORPHANED_TIMEOUT = 15 * 1000;\n\nconst safeSend = (msg: IPCMessage): boolean => {\n if (process.connected && process.send) {\n process.send(msg);\n return true;\n }\n return false;\n};\n\ntype JobTask = {\n ctx: JobContext;\n task: Promise<void>;\n};\n\nclass PendingInference {\n promise = new Promise<{ requestId: string; data: unknown; error?: Error }>((resolve) => {\n this.resolve = resolve; // this is how JavaScript lets you resolve promises externally\n });\n resolve(arg: { requestId: string; data: unknown; error?: Error }) {\n arg; // useless call to counteract TypeScript E6133\n }\n}\n\nclass InfClient implements InferenceExecutor {\n #requests: { [id: string]: PendingInference } = {};\n\n constructor() {\n process.on('message', (msg: IPCMessage) => {\n switch (msg.case) {\n case 'inferenceResponse':\n const fut = this.#requests[msg.value.requestId];\n delete this.#requests[msg.value.requestId];\n if (!fut) {\n log().child({ resp: msg.value }).warn('received unexpected inference response');\n return;\n }\n fut.resolve(msg.value);\n break;\n }\n });\n }\n\n async doInference(method: string, data: unknown): Promise<unknown> {\n const requestId = shortuuid('inference_job_');\n if (!safeSend({ case: 'inferenceRequest', value: { requestId, method, data } })) {\n throw new Error('IPC channel closed');\n }\n\n this.#requests[requestId] = new PendingInference();\n const resp = await this.#requests[requestId]!.promise;\n if (resp.error) {\n throw new Error(`inference of ${method} failed: ${resp.error.message}`);\n }\n return resp.data;\n }\n}\n\nconst startJob = (\n proc: JobProcess,\n func: (ctx: JobContext) => Promise<void>,\n info: RunningJobInfo,\n closeEvent: EventEmitter,\n logger: Logger,\n joinFuture: Future,\n): JobTask => {\n let connect = false;\n let shutdown = false;\n\n const room = new Room();\n room.on(RoomEvent.Disconnected, () => {\n if (!shutdown) {\n closeEvent.emit('close', false);\n }\n });\n\n const onConnect = () => {\n connect = true;\n };\n const onShutdown = (reason: string) => {\n shutdown = true;\n closeEvent.emit('close', true, reason);\n };\n\n const ctx = new JobContext(proc, info, room, onConnect, onShutdown, new InfClient());\n\n const task = (async () => {\n const unconnectedTimeout = setTimeout(() => {\n if (!(connect || shutdown)) {\n logger.warn(\n 'room not connect after job_entry was called after 10 seconds, ',\n 'did you forget to call ctx.connect()?',\n );\n }\n }, 10000);\n\n // Run the job function within the AsyncLocalStorage context\n await runWithJobContextAsync(ctx, async () => {\n const { tracer, traceTypes } = await import('../telemetry/index.js');\n return tracer.startActiveSpan(\n async (span) => {\n span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);\n span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);\n span.setAttribute(traceTypes.ATTR_ROOM_NAME, info.job.room?.name ?? '');\n return func(ctx);\n },\n { name: 'job_entrypoint' },\n );\n }).finally(() => {\n clearTimeout(unconnectedTimeout);\n });\n\n await once(closeEvent, 'close').then((close) => {\n logger.debug('shutting down');\n shutdown = true;\n safeSend({ case: 'exiting', value: { reason: close[1] } });\n });\n\n // Close the primary agent session if it exists\n if (ctx._primaryAgentSession) {\n await ctx._primaryAgentSession.close();\n }\n\n // Generate and save/upload session report\n await ctx._onSessionEnd();\n\n await room.disconnect();\n logger.debug('disconnected from room');\n\n const shutdownTasks = [];\n for (const callback of ctx.shutdownCallbacks) {\n shutdownTasks.push(callback());\n }\n await Promise.all(shutdownTasks).catch((error) =>\n logger.error({ error }, 'error while shutting down the job'),\n );\n\n safeSend({ case: 'done', value: undefined });\n joinFuture.resolve();\n })();\n\n return { ctx, task };\n};\n\n(async () => {\n if (process.send) {\n const join = new Future();\n\n // process.argv:\n // [0] `node'\n // [1] import.meta.filename\n // [2] import.meta.filename of function containing entry file\n const moduleFile = process.argv[2];\n const agent: Agent = await import(pathToFileURL(moduleFile!).pathname).then((module) => {\n // Handle both ESM (module.default is the agent) and CJS (module.default.default is the agent)\n const agent =\n typeof module.default === 'function' || isAgent(module.default)\n ? module.default\n : module.default?.default;\n if (agent === undefined || !isAgent(agent)) {\n throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`);\n }\n return agent;\n });\n if (!agent.prewarm) {\n agent.prewarm = defaultInitializeProcessFunc;\n }\n\n // don't do anything on C-c\n // this is handled in cli, triggering a termination of all child processes at once.\n process.on('SIGINT', () => {\n logger.debug('SIGINT received in job proc');\n });\n\n // don't do anything on SIGTERM\n // Render uses SIGTERM in autoscale, this ensures the processes are properly drained if needed\n process.on('SIGTERM', () => {\n logger.debug('SIGTERM received in job proc');\n });\n\n await once(process, 'message').then(([msg]: IPCMessage[]) => {\n msg = msg!;\n if (msg.case !== 'initializeRequest') {\n throw new Error('first message must be InitializeRequest');\n }\n initializeLogger(msg.value.loggerOptions);\n });\n const proc = new JobProcess();\n let logger = log().child({ pid: proc.pid });\n\n process.on('unhandledRejection', (reason) => {\n logger.debug({ error: reason }, 'Unhandled promise rejection');\n });\n\n logger.debug('initializing job runner');\n await agent.prewarm(proc);\n logger.debug('job runner initialized');\n safeSend({ case: 'initializeResponse', value: undefined });\n\n let job: JobTask | undefined = undefined;\n const closeEvent = new EventEmitter();\n\n const orphanedTimeout = setTimeout(() => {\n logger.warn('job process orphaned, shutting down.');\n join.resolve();\n }, ORPHANED_TIMEOUT);\n\n const messageHandler = (msg: IPCMessage) => {\n switch (msg.case) {\n case 'pingRequest': {\n orphanedTimeout.refresh();\n safeSend({\n case: 'pongResponse',\n value: { lastTimestamp: msg.value.timestamp, timestamp: Date.now() },\n });\n break;\n }\n case 'startJobRequest': {\n if (job) {\n throw new Error('job task already running');\n }\n\n logger = logger.child({ jobID: msg.value.runningJob.job.id });\n\n job = startJob(proc, agent.entry, msg.value.runningJob, closeEvent, logger, join);\n logger.debug('job started');\n break;\n }\n case 'shutdownRequest': {\n if (!job) {\n join.resolve();\n }\n closeEvent.emit('close', 'shutdownRequest');\n clearTimeout(orphanedTimeout);\n process.off('message', messageHandler);\n }\n }\n };\n\n process.on('message', messageHandler);\n\n await join.await;\n\n // Dispose native FFI resources (Rust FfiServer, tokio runtimes, libwebrtc)\n // before process.exit() to prevent libc++abi mutex crash during teardown.\n // Without this, process.exit() can kill the process while native threads are\n // still running, causing: \"mutex lock failed: Invalid argument\"\n // See: https://github.com/livekit/node-sdks/issues/564\n try {\n await dispose();\n logger.debug('native resources disposed');\n } catch (error) {\n logger.warn({ error }, 'failed to dispose native resources');\n }\n\n logger.debug('Job process shutdown');\n process.exit(0);\n }\n})();\n"],"mappings":"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","names":["module","agent"]}
|
|
1
|
+
{"version":3,"sources":["../../src/ipc/job_proc_lazy_main.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Room, RoomEvent, dispose } from '@livekit/rtc-node';\nimport { EventEmitter, once } from 'node:events';\nimport { pathToFileURL } from 'node:url';\nimport type { Logger } from 'pino';\nimport { type Agent, isAgent } from '../generator.js';\nimport { JobContext, JobProcess, type RunningJobInfo, runWithJobContextAsync } from '../job.js';\nimport { initializeLogger, log } from '../log.js';\nimport { Future, shortuuid } from '../utils.js';\nimport { defaultInitializeProcessFunc } from '../worker.js';\nimport type { InferenceExecutor } from './inference_executor.js';\nimport type { IPCMessage } from './message.js';\n\nconst ORPHANED_TIMEOUT = 15 * 1000;\n\nconst safeSend = (msg: IPCMessage): boolean => {\n try {\n if (process.connected && process.send) {\n process.send(msg);\n return true;\n }\n return false;\n } catch (error) {\n // Channel closed is expected during graceful shutdown\n // Log at debug level to avoid noise in production logs\n if (error instanceof Error && error.message.includes('Channel closed')) {\n log().debug({ msgCase: msg.case }, 'IPC channel closed, message not sent');\n } else {\n log().error({ error, msgCase: msg.case }, 'IPC send failed unexpectedly');\n }\n return false;\n }\n};\n\ntype JobTask = {\n ctx: JobContext;\n task: Promise<void>;\n};\n\nclass PendingInference {\n promise = new Promise<{ requestId: string; data: unknown; error?: Error }>((resolve) => {\n this.resolve = resolve; // this is how JavaScript lets you resolve promises externally\n });\n resolve(arg: { requestId: string; data: unknown; error?: Error }) {\n arg; // useless call to counteract TypeScript E6133\n }\n}\n\nclass InfClient implements InferenceExecutor {\n #requests: { [id: string]: PendingInference } = {};\n\n constructor() {\n process.on('message', (msg: IPCMessage) => {\n switch (msg.case) {\n case 'inferenceResponse':\n const fut = this.#requests[msg.value.requestId];\n delete this.#requests[msg.value.requestId];\n if (!fut) {\n log().child({ resp: msg.value }).warn('received unexpected inference response');\n return;\n }\n fut.resolve(msg.value);\n break;\n }\n });\n }\n\n async doInference(method: string, data: unknown): Promise<unknown> {\n const requestId = shortuuid('inference_job_');\n if (!safeSend({ case: 'inferenceRequest', value: { requestId, method, data } })) {\n throw new Error('IPC channel closed');\n }\n\n this.#requests[requestId] = new PendingInference();\n const resp = await this.#requests[requestId]!.promise;\n if (resp.error) {\n throw new Error(`inference of ${method} failed: ${resp.error.message}`);\n }\n return resp.data;\n }\n}\n\nconst startJob = (\n proc: JobProcess,\n func: (ctx: JobContext) => Promise<void>,\n info: RunningJobInfo,\n closeEvent: EventEmitter,\n logger: Logger,\n joinFuture: Future,\n): JobTask => {\n let connect = false;\n let shutdown = false;\n\n const room = new Room();\n room.on(RoomEvent.Disconnected, () => {\n if (!shutdown) {\n closeEvent.emit('close', false);\n }\n });\n\n const onConnect = () => {\n connect = true;\n };\n const onShutdown = (reason: string) => {\n shutdown = true;\n closeEvent.emit('close', true, reason);\n };\n\n const ctx = new JobContext(proc, info, room, onConnect, onShutdown, new InfClient());\n\n const task = (async () => {\n const unconnectedTimeout = setTimeout(() => {\n if (!(connect || shutdown)) {\n logger.warn(\n 'room not connect after job_entry was called after 10 seconds, ',\n 'did you forget to call ctx.connect()?',\n );\n }\n }, 10000);\n\n try {\n // Run the job function within the AsyncLocalStorage context\n await runWithJobContextAsync(ctx, async () => {\n const { tracer, traceTypes } = await import('../telemetry/index.js');\n return tracer.startActiveSpan(\n async (span) => {\n span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);\n span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);\n span.setAttribute(traceTypes.ATTR_ROOM_NAME, info.job.room?.name ?? '');\n return func(ctx);\n },\n { name: 'job_entrypoint' },\n );\n }).finally(() => {\n clearTimeout(unconnectedTimeout);\n });\n\n await once(closeEvent, 'close').then((close) => {\n logger.debug('shutting down');\n shutdown = true;\n safeSend({ case: 'exiting', value: { reason: close[1] } });\n });\n } catch (error) {\n logger.error({ error }, 'error in entry function');\n shutdown = true;\n safeSend({\n case: 'exiting',\n value: { reason: error instanceof Error ? error.message : String(error) },\n });\n }\n\n // Close the primary agent session if it exists\n if (ctx._primaryAgentSession) {\n await ctx._primaryAgentSession.close();\n }\n\n // Generate and save/upload session report\n await ctx._onSessionEnd();\n\n await room.disconnect();\n logger.debug('disconnected from room');\n\n const shutdownTasks = [];\n for (const callback of ctx.shutdownCallbacks) {\n shutdownTasks.push(callback());\n }\n await Promise.all(shutdownTasks).catch((error) =>\n logger.error({ error }, 'error while shutting down the job'),\n );\n\n safeSend({ case: 'done', value: undefined });\n joinFuture.resolve();\n })();\n\n return { ctx, task };\n};\n\n(async () => {\n if (process.send) {\n const join = new Future();\n\n // process.argv:\n // [0] `node'\n // [1] import.meta.filename\n // [2] import.meta.filename of function containing entry file\n const moduleFile = process.argv[2];\n const agent: Agent = await import(pathToFileURL(moduleFile!).pathname).then((module) => {\n // Handle both ESM (module.default is the agent) and CJS (module.default.default is the agent)\n const agent =\n typeof module.default === 'function' || isAgent(module.default)\n ? module.default\n : module.default?.default;\n if (agent === undefined || !isAgent(agent)) {\n throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`);\n }\n return agent;\n });\n if (!agent.prewarm) {\n agent.prewarm = defaultInitializeProcessFunc;\n }\n\n // don't do anything on C-c\n // this is handled in cli, triggering a termination of all child processes at once.\n process.on('SIGINT', () => {\n logger.debug('SIGINT received in job proc');\n });\n\n // don't do anything on SIGTERM\n // Render uses SIGTERM in autoscale, this ensures the processes are properly drained if needed\n process.on('SIGTERM', () => {\n logger.debug('SIGTERM received in job proc');\n });\n\n await once(process, 'message').then(([msg]: IPCMessage[]) => {\n msg = msg!;\n if (msg.case !== 'initializeRequest') {\n throw new Error('first message must be InitializeRequest');\n }\n initializeLogger(msg.value.loggerOptions);\n });\n const proc = new JobProcess();\n let logger = log().child({ pid: proc.pid });\n\n process.on('unhandledRejection', (reason) => {\n logger.debug({ error: reason }, 'Unhandled promise rejection');\n });\n\n logger.debug('initializing job runner');\n await agent.prewarm(proc);\n logger.debug('job runner initialized');\n safeSend({ case: 'initializeResponse', value: undefined });\n\n let job: JobTask | undefined = undefined;\n const closeEvent = new EventEmitter();\n\n const orphanedTimeout = setTimeout(() => {\n logger.warn('job process orphaned, shutting down.');\n join.resolve();\n }, ORPHANED_TIMEOUT);\n\n const messageHandler = (msg: IPCMessage) => {\n switch (msg.case) {\n case 'pingRequest': {\n orphanedTimeout.refresh();\n safeSend({\n case: 'pongResponse',\n value: { lastTimestamp: msg.value.timestamp, timestamp: Date.now() },\n });\n break;\n }\n case 'startJobRequest': {\n if (job) {\n throw new Error('job task already running');\n }\n\n logger = logger.child({ jobID: msg.value.runningJob.job.id });\n\n job = startJob(proc, agent.entry, msg.value.runningJob, closeEvent, logger, join);\n logger.debug('job started');\n break;\n }\n case 'shutdownRequest': {\n if (!job) {\n join.resolve();\n }\n closeEvent.emit('close', 'shutdownRequest');\n clearTimeout(orphanedTimeout);\n process.off('message', messageHandler);\n }\n }\n };\n\n process.on('message', messageHandler);\n\n await join.await;\n\n // Dispose native FFI resources (Rust FfiServer, tokio runtimes, libwebrtc)\n // before process.exit() to prevent libc++abi mutex crash during teardown.\n // Without this, process.exit() can kill the process while native threads are\n // still running, causing: \"mutex lock failed: Invalid argument\"\n // See: https://github.com/livekit/node-sdks/issues/564\n try {\n await dispose();\n logger.debug('native resources disposed');\n } catch (error) {\n logger.warn({ error }, 'failed to dispose native resources');\n }\n\n logger.debug('Job process shutdown');\n process.exit(0);\n }\n})();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAGA,sBAAyC;AACzC,yBAAmC;AACnC,sBAA8B;AAE9B,uBAAoC;AACpC,iBAAoF;AACpF,iBAAsC;AACtC,mBAAkC;AAClC,oBAA6C;AAI7C,MAAM,mBAAmB,KAAK;AAE9B,MAAM,WAAW,CAAC,QAA6B;AAC7C,MAAI;AACF,QAAI,QAAQ,aAAa,QAAQ,MAAM;AACrC,cAAQ,KAAK,GAAG;AAChB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AAGd,QAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,gBAAgB,GAAG;AACtE,0BAAI,EAAE,MAAM,EAAE,SAAS,IAAI,KAAK,GAAG,sCAAsC;AAAA,IAC3E,OAAO;AACL,0BAAI,EAAE,MAAM,EAAE,OAAO,SAAS,IAAI,KAAK,GAAG,8BAA8B;AAAA,IAC1E;AACA,WAAO;AAAA,EACT;AACF;AAOA,MAAM,iBAAiB;AAAA,EACrB,UAAU,IAAI,QAA6D,CAAC,YAAY;AACtF,SAAK,UAAU;AAAA,EACjB,CAAC;AAAA,EACD,QAAQ,KAA0D;AAChE;AAAA,EACF;AACF;AAEA,MAAM,UAAuC;AAAA,EAC3C,YAAgD,CAAC;AAAA,EAEjD,cAAc;AACZ,YAAQ,GAAG,WAAW,CAAC,QAAoB;AACzC,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,gBAAM,MAAM,KAAK,UAAU,IAAI,MAAM,SAAS;AAC9C,iBAAO,KAAK,UAAU,IAAI,MAAM,SAAS;AACzC,cAAI,CAAC,KAAK;AACR,gCAAI,EAAE,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,EAAE,KAAK,wCAAwC;AAC9E;AAAA,UACF;AACA,cAAI,QAAQ,IAAI,KAAK;AACrB;AAAA,MACJ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,QAAgB,MAAiC;AACjE,UAAM,gBAAY,wBAAU,gBAAgB;AAC5C,QAAI,CAAC,SAAS,EAAE,MAAM,oBAAoB,OAAO,EAAE,WAAW,QAAQ,KAAK,EAAE,CAAC,GAAG;AAC/E,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAEA,SAAK,UAAU,SAAS,IAAI,IAAI,iBAAiB;AACjD,UAAM,OAAO,MAAM,KAAK,UAAU,SAAS,EAAG;AAC9C,QAAI,KAAK,OAAO;AACd,YAAM,IAAI,MAAM,gBAAgB,MAAM,YAAY,KAAK,MAAM,OAAO,EAAE;AAAA,IACxE;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAEA,MAAM,WAAW,CACf,MACA,MACA,MACA,YACA,QACA,eACY;AACZ,MAAI,UAAU;AACd,MAAI,WAAW;AAEf,QAAM,OAAO,IAAI,qBAAK;AACtB,OAAK,GAAG,0BAAU,cAAc,MAAM;AACpC,QAAI,CAAC,UAAU;AACb,iBAAW,KAAK,SAAS,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,YAAY,MAAM;AACtB,cAAU;AAAA,EACZ;AACA,QAAM,aAAa,CAAC,WAAmB;AACrC,eAAW;AACX,eAAW,KAAK,SAAS,MAAM,MAAM;AAAA,EACvC;AAEA,QAAM,MAAM,IAAI,sBAAW,MAAM,MAAM,MAAM,WAAW,YAAY,IAAI,UAAU,CAAC;AAEnF,QAAM,QAAQ,YAAY;AACxB,UAAM,qBAAqB,WAAW,MAAM;AAC1C,UAAI,EAAE,WAAW,WAAW;AAC1B,eAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,GAAK;AAER,QAAI;AAEF,gBAAM,mCAAuB,KAAK,YAAY;AAC5C,cAAM,EAAE,QAAQ,WAAW,IAAI,MAAM,OAAO,uBAAuB;AACnE,eAAO,OAAO;AAAA,UACZ,OAAO,SAAS;AA/H1B;AAgIY,iBAAK,aAAa,WAAW,aAAa,KAAK,IAAI,EAAE;AACrD,iBAAK,aAAa,WAAW,iBAAiB,KAAK,IAAI,SAAS;AAChE,iBAAK,aAAa,WAAW,kBAAgB,UAAK,IAAI,SAAT,mBAAe,SAAQ,EAAE;AACtE,mBAAO,KAAK,GAAG;AAAA,UACjB;AAAA,UACA,EAAE,MAAM,iBAAiB;AAAA,QAC3B;AAAA,MACF,CAAC,EAAE,QAAQ,MAAM;AACf,qBAAa,kBAAkB;AAAA,MACjC,CAAC;AAED,gBAAM,yBAAK,YAAY,OAAO,EAAE,KAAK,CAAC,UAAU;AAC9C,eAAO,MAAM,eAAe;AAC5B,mBAAW;AACX,iBAAS,EAAE,MAAM,WAAW,OAAO,EAAE,QAAQ,MAAM,CAAC,EAAE,EAAE,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH,SAAS,OAAO;AACd,aAAO,MAAM,EAAE,MAAM,GAAG,yBAAyB;AACjD,iBAAW;AACX,eAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO,EAAE,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AAAA,MAC1E,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,sBAAsB;AAC5B,YAAM,IAAI,qBAAqB,MAAM;AAAA,IACvC;AAGA,UAAM,IAAI,cAAc;AAExB,UAAM,KAAK,WAAW;AACtB,WAAO,MAAM,wBAAwB;AAErC,UAAM,gBAAgB,CAAC;AACvB,eAAW,YAAY,IAAI,mBAAmB;AAC5C,oBAAc,KAAK,SAAS,CAAC;AAAA,IAC/B;AACA,UAAM,QAAQ,IAAI,aAAa,EAAE;AAAA,MAAM,CAAC,UACtC,OAAO,MAAM,EAAE,MAAM,GAAG,mCAAmC;AAAA,IAC7D;AAEA,aAAS,EAAE,MAAM,QAAQ,OAAO,OAAU,CAAC;AAC3C,eAAW,QAAQ;AAAA,EACrB,GAAG;AAEH,SAAO,EAAE,KAAK,KAAK;AACrB;AAAA,CAEC,YAAY;AACX,MAAI,QAAQ,MAAM;AAChB,UAAM,OAAO,IAAI,oBAAO;AAMxB,UAAM,aAAa,QAAQ,KAAK,CAAC;AACjC,UAAM,QAAe,MAAM,WAAO,+BAAc,UAAW,EAAE,UAAU,KAAK,CAACA,YAAW;AA5L5F;AA8LM,YAAMC,SACJ,OAAOD,QAAO,YAAY,kBAAc,0BAAQA,QAAO,OAAO,IAC1DA,QAAO,WACP,KAAAA,QAAO,YAAP,mBAAgB;AACtB,UAAIC,WAAU,UAAa,KAAC,0BAAQA,MAAK,GAAG;AAC1C,cAAM,IAAI,MAAM,8DAA8D,UAAU,EAAE;AAAA,MAC5F;AACA,aAAOA;AAAA,IACT,CAAC;AACD,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,UAAU;AAAA,IAClB;AAIA,YAAQ,GAAG,UAAU,MAAM;AACzB,aAAO,MAAM,6BAA6B;AAAA,IAC5C,CAAC;AAID,YAAQ,GAAG,WAAW,MAAM;AAC1B,aAAO,MAAM,8BAA8B;AAAA,IAC7C,CAAC;AAED,cAAM,yBAAK,SAAS,SAAS,EAAE,KAAK,CAAC,CAAC,GAAG,MAAoB;AAC3D,YAAM;AACN,UAAI,IAAI,SAAS,qBAAqB;AACpC,cAAM,IAAI,MAAM,yCAAyC;AAAA,MAC3D;AACA,uCAAiB,IAAI,MAAM,aAAa;AAAA,IAC1C,CAAC;AACD,UAAM,OAAO,IAAI,sBAAW;AAC5B,QAAI,aAAS,gBAAI,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;AAE1C,YAAQ,GAAG,sBAAsB,CAAC,WAAW;AAC3C,aAAO,MAAM,EAAE,OAAO,OAAO,GAAG,6BAA6B;AAAA,IAC/D,CAAC;AAED,WAAO,MAAM,yBAAyB;AACtC,UAAM,MAAM,QAAQ,IAAI;AACxB,WAAO,MAAM,wBAAwB;AACrC,aAAS,EAAE,MAAM,sBAAsB,OAAO,OAAU,CAAC;AAEzD,QAAI,MAA2B;AAC/B,UAAM,aAAa,IAAI,gCAAa;AAEpC,UAAM,kBAAkB,WAAW,MAAM;AACvC,aAAO,KAAK,sCAAsC;AAClD,WAAK,QAAQ;AAAA,IACf,GAAG,gBAAgB;AAEnB,UAAM,iBAAiB,CAAC,QAAoB;AAC1C,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK,eAAe;AAClB,0BAAgB,QAAQ;AACxB,mBAAS;AAAA,YACP,MAAM;AAAA,YACN,OAAO,EAAE,eAAe,IAAI,MAAM,WAAW,WAAW,KAAK,IAAI,EAAE;AAAA,UACrE,CAAC;AACD;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,cAAI,KAAK;AACP,kBAAM,IAAI,MAAM,0BAA0B;AAAA,UAC5C;AAEA,mBAAS,OAAO,MAAM,EAAE,OAAO,IAAI,MAAM,WAAW,IAAI,GAAG,CAAC;AAE5D,gBAAM,SAAS,MAAM,MAAM,OAAO,IAAI,MAAM,YAAY,YAAY,QAAQ,IAAI;AAChF,iBAAO,MAAM,aAAa;AAC1B;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,cAAI,CAAC,KAAK;AACR,iBAAK,QAAQ;AAAA,UACf;AACA,qBAAW,KAAK,SAAS,iBAAiB;AAC1C,uBAAa,eAAe;AAC5B,kBAAQ,IAAI,WAAW,cAAc;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,GAAG,WAAW,cAAc;AAEpC,UAAM,KAAK;AAOX,QAAI;AACF,gBAAM,yBAAQ;AACd,aAAO,MAAM,2BAA2B;AAAA,IAC1C,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,MAAM,GAAG,oCAAoC;AAAA,IAC7D;AAEA,WAAO,MAAM,sBAAsB;AACnC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,GAAG;","names":["module","agent"]}
|
|
@@ -8,11 +8,20 @@ import { Future, shortuuid } from "../utils.js";
|
|
|
8
8
|
import { defaultInitializeProcessFunc } from "../worker.js";
|
|
9
9
|
const ORPHANED_TIMEOUT = 15 * 1e3;
|
|
10
10
|
const safeSend = (msg) => {
|
|
11
|
-
|
|
12
|
-
process.send
|
|
13
|
-
|
|
11
|
+
try {
|
|
12
|
+
if (process.connected && process.send) {
|
|
13
|
+
process.send(msg);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error instanceof Error && error.message.includes("Channel closed")) {
|
|
19
|
+
log().debug({ msgCase: msg.case }, "IPC channel closed, message not sent");
|
|
20
|
+
} else {
|
|
21
|
+
log().error({ error, msgCase: msg.case }, "IPC send failed unexpectedly");
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
14
24
|
}
|
|
15
|
-
return false;
|
|
16
25
|
};
|
|
17
26
|
class PendingInference {
|
|
18
27
|
promise = new Promise((resolve) => {
|
|
@@ -78,26 +87,35 @@ const startJob = (proc, func, info, closeEvent, logger, joinFuture) => {
|
|
|
78
87
|
);
|
|
79
88
|
}
|
|
80
89
|
}, 1e4);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
try {
|
|
91
|
+
await runWithJobContextAsync(ctx, async () => {
|
|
92
|
+
const { tracer, traceTypes } = await import("../telemetry/index.js");
|
|
93
|
+
return tracer.startActiveSpan(
|
|
94
|
+
async (span) => {
|
|
95
|
+
var _a;
|
|
96
|
+
span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);
|
|
97
|
+
span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);
|
|
98
|
+
span.setAttribute(traceTypes.ATTR_ROOM_NAME, ((_a = info.job.room) == null ? void 0 : _a.name) ?? "");
|
|
99
|
+
return func(ctx);
|
|
100
|
+
},
|
|
101
|
+
{ name: "job_entrypoint" }
|
|
102
|
+
);
|
|
103
|
+
}).finally(() => {
|
|
104
|
+
clearTimeout(unconnectedTimeout);
|
|
105
|
+
});
|
|
106
|
+
await once(closeEvent, "close").then((close) => {
|
|
107
|
+
logger.debug("shutting down");
|
|
108
|
+
shutdown = true;
|
|
109
|
+
safeSend({ case: "exiting", value: { reason: close[1] } });
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
logger.error({ error }, "error in entry function");
|
|
98
113
|
shutdown = true;
|
|
99
|
-
safeSend({
|
|
100
|
-
|
|
114
|
+
safeSend({
|
|
115
|
+
case: "exiting",
|
|
116
|
+
value: { reason: error instanceof Error ? error.message : String(error) }
|
|
117
|
+
});
|
|
118
|
+
}
|
|
101
119
|
if (ctx._primaryAgentSession) {
|
|
102
120
|
await ctx._primaryAgentSession.close();
|
|
103
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ipc/job_proc_lazy_main.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Room, RoomEvent, dispose } from '@livekit/rtc-node';\nimport { EventEmitter, once } from 'node:events';\nimport { pathToFileURL } from 'node:url';\nimport type { Logger } from 'pino';\nimport { type Agent, isAgent } from '../generator.js';\nimport { JobContext, JobProcess, type RunningJobInfo, runWithJobContextAsync } from '../job.js';\nimport { initializeLogger, log } from '../log.js';\nimport { Future, shortuuid } from '../utils.js';\nimport { defaultInitializeProcessFunc } from '../worker.js';\nimport type { InferenceExecutor } from './inference_executor.js';\nimport type { IPCMessage } from './message.js';\n\nconst ORPHANED_TIMEOUT = 15 * 1000;\n\nconst safeSend = (msg: IPCMessage): boolean => {\n if (process.connected && process.send) {\n process.send(msg);\n return true;\n }\n return false;\n};\n\ntype JobTask = {\n ctx: JobContext;\n task: Promise<void>;\n};\n\nclass PendingInference {\n promise = new Promise<{ requestId: string; data: unknown; error?: Error }>((resolve) => {\n this.resolve = resolve; // this is how JavaScript lets you resolve promises externally\n });\n resolve(arg: { requestId: string; data: unknown; error?: Error }) {\n arg; // useless call to counteract TypeScript E6133\n }\n}\n\nclass InfClient implements InferenceExecutor {\n #requests: { [id: string]: PendingInference } = {};\n\n constructor() {\n process.on('message', (msg: IPCMessage) => {\n switch (msg.case) {\n case 'inferenceResponse':\n const fut = this.#requests[msg.value.requestId];\n delete this.#requests[msg.value.requestId];\n if (!fut) {\n log().child({ resp: msg.value }).warn('received unexpected inference response');\n return;\n }\n fut.resolve(msg.value);\n break;\n }\n });\n }\n\n async doInference(method: string, data: unknown): Promise<unknown> {\n const requestId = shortuuid('inference_job_');\n if (!safeSend({ case: 'inferenceRequest', value: { requestId, method, data } })) {\n throw new Error('IPC channel closed');\n }\n\n this.#requests[requestId] = new PendingInference();\n const resp = await this.#requests[requestId]!.promise;\n if (resp.error) {\n throw new Error(`inference of ${method} failed: ${resp.error.message}`);\n }\n return resp.data;\n }\n}\n\nconst startJob = (\n proc: JobProcess,\n func: (ctx: JobContext) => Promise<void>,\n info: RunningJobInfo,\n closeEvent: EventEmitter,\n logger: Logger,\n joinFuture: Future,\n): JobTask => {\n let connect = false;\n let shutdown = false;\n\n const room = new Room();\n room.on(RoomEvent.Disconnected, () => {\n if (!shutdown) {\n closeEvent.emit('close', false);\n }\n });\n\n const onConnect = () => {\n connect = true;\n };\n const onShutdown = (reason: string) => {\n shutdown = true;\n closeEvent.emit('close', true, reason);\n };\n\n const ctx = new JobContext(proc, info, room, onConnect, onShutdown, new InfClient());\n\n const task = (async () => {\n const unconnectedTimeout = setTimeout(() => {\n if (!(connect || shutdown)) {\n logger.warn(\n 'room not connect after job_entry was called after 10 seconds, ',\n 'did you forget to call ctx.connect()?',\n );\n }\n }, 10000);\n\n // Run the job function within the AsyncLocalStorage context\n await runWithJobContextAsync(ctx, async () => {\n const { tracer, traceTypes } = await import('../telemetry/index.js');\n return tracer.startActiveSpan(\n async (span) => {\n span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);\n span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);\n span.setAttribute(traceTypes.ATTR_ROOM_NAME, info.job.room?.name ?? '');\n return func(ctx);\n },\n { name: 'job_entrypoint' },\n );\n }).finally(() => {\n clearTimeout(unconnectedTimeout);\n });\n\n await once(closeEvent, 'close').then((close) => {\n logger.debug('shutting down');\n shutdown = true;\n safeSend({ case: 'exiting', value: { reason: close[1] } });\n });\n\n // Close the primary agent session if it exists\n if (ctx._primaryAgentSession) {\n await ctx._primaryAgentSession.close();\n }\n\n // Generate and save/upload session report\n await ctx._onSessionEnd();\n\n await room.disconnect();\n logger.debug('disconnected from room');\n\n const shutdownTasks = [];\n for (const callback of ctx.shutdownCallbacks) {\n shutdownTasks.push(callback());\n }\n await Promise.all(shutdownTasks).catch((error) =>\n logger.error({ error }, 'error while shutting down the job'),\n );\n\n safeSend({ case: 'done', value: undefined });\n joinFuture.resolve();\n })();\n\n return { ctx, task };\n};\n\n(async () => {\n if (process.send) {\n const join = new Future();\n\n // process.argv:\n // [0] `node'\n // [1] import.meta.filename\n // [2] import.meta.filename of function containing entry file\n const moduleFile = process.argv[2];\n const agent: Agent = await import(pathToFileURL(moduleFile!).pathname).then((module) => {\n // Handle both ESM (module.default is the agent) and CJS (module.default.default is the agent)\n const agent =\n typeof module.default === 'function' || isAgent(module.default)\n ? module.default\n : module.default?.default;\n if (agent === undefined || !isAgent(agent)) {\n throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`);\n }\n return agent;\n });\n if (!agent.prewarm) {\n agent.prewarm = defaultInitializeProcessFunc;\n }\n\n // don't do anything on C-c\n // this is handled in cli, triggering a termination of all child processes at once.\n process.on('SIGINT', () => {\n logger.debug('SIGINT received in job proc');\n });\n\n // don't do anything on SIGTERM\n // Render uses SIGTERM in autoscale, this ensures the processes are properly drained if needed\n process.on('SIGTERM', () => {\n logger.debug('SIGTERM received in job proc');\n });\n\n await once(process, 'message').then(([msg]: IPCMessage[]) => {\n msg = msg!;\n if (msg.case !== 'initializeRequest') {\n throw new Error('first message must be InitializeRequest');\n }\n initializeLogger(msg.value.loggerOptions);\n });\n const proc = new JobProcess();\n let logger = log().child({ pid: proc.pid });\n\n process.on('unhandledRejection', (reason) => {\n logger.debug({ error: reason }, 'Unhandled promise rejection');\n });\n\n logger.debug('initializing job runner');\n await agent.prewarm(proc);\n logger.debug('job runner initialized');\n safeSend({ case: 'initializeResponse', value: undefined });\n\n let job: JobTask | undefined = undefined;\n const closeEvent = new EventEmitter();\n\n const orphanedTimeout = setTimeout(() => {\n logger.warn('job process orphaned, shutting down.');\n join.resolve();\n }, ORPHANED_TIMEOUT);\n\n const messageHandler = (msg: IPCMessage) => {\n switch (msg.case) {\n case 'pingRequest': {\n orphanedTimeout.refresh();\n safeSend({\n case: 'pongResponse',\n value: { lastTimestamp: msg.value.timestamp, timestamp: Date.now() },\n });\n break;\n }\n case 'startJobRequest': {\n if (job) {\n throw new Error('job task already running');\n }\n\n logger = logger.child({ jobID: msg.value.runningJob.job.id });\n\n job = startJob(proc, agent.entry, msg.value.runningJob, closeEvent, logger, join);\n logger.debug('job started');\n break;\n }\n case 'shutdownRequest': {\n if (!job) {\n join.resolve();\n }\n closeEvent.emit('close', 'shutdownRequest');\n clearTimeout(orphanedTimeout);\n process.off('message', messageHandler);\n }\n }\n };\n\n process.on('message', messageHandler);\n\n await join.await;\n\n // Dispose native FFI resources (Rust FfiServer, tokio runtimes, libwebrtc)\n // before process.exit() to prevent libc++abi mutex crash during teardown.\n // Without this, process.exit() can kill the process while native threads are\n // still running, causing: \"mutex lock failed: Invalid argument\"\n // See: https://github.com/livekit/node-sdks/issues/564\n try {\n await dispose();\n logger.debug('native resources disposed');\n } catch (error) {\n logger.warn({ error }, 'failed to dispose native resources');\n }\n\n logger.debug('Job process shutdown');\n process.exit(0);\n }\n})();\n"],"mappings":"AAGA,SAAS,MAAM,WAAW,eAAe;AACzC,SAAS,cAAc,YAAY;AACnC,SAAS,qBAAqB;AAE9B,SAAqB,eAAe;AACpC,SAAS,YAAY,YAAiC,8BAA8B;AACpF,SAAS,kBAAkB,WAAW;AACtC,SAAS,QAAQ,iBAAiB;AAClC,SAAS,oCAAoC;AAI7C,MAAM,mBAAmB,KAAK;AAE9B,MAAM,WAAW,CAAC,QAA6B;AAC7C,MAAI,QAAQ,aAAa,QAAQ,MAAM;AACrC,YAAQ,KAAK,GAAG;AAChB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOA,MAAM,iBAAiB;AAAA,EACrB,UAAU,IAAI,QAA6D,CAAC,YAAY;AACtF,SAAK,UAAU;AAAA,EACjB,CAAC;AAAA,EACD,QAAQ,KAA0D;AAChE;AAAA,EACF;AACF;AAEA,MAAM,UAAuC;AAAA,EAC3C,YAAgD,CAAC;AAAA,EAEjD,cAAc;AACZ,YAAQ,GAAG,WAAW,CAAC,QAAoB;AACzC,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,gBAAM,MAAM,KAAK,UAAU,IAAI,MAAM,SAAS;AAC9C,iBAAO,KAAK,UAAU,IAAI,MAAM,SAAS;AACzC,cAAI,CAAC,KAAK;AACR,gBAAI,EAAE,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,EAAE,KAAK,wCAAwC;AAC9E;AAAA,UACF;AACA,cAAI,QAAQ,IAAI,KAAK;AACrB;AAAA,MACJ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,QAAgB,MAAiC;AACjE,UAAM,YAAY,UAAU,gBAAgB;AAC5C,QAAI,CAAC,SAAS,EAAE,MAAM,oBAAoB,OAAO,EAAE,WAAW,QAAQ,KAAK,EAAE,CAAC,GAAG;AAC/E,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAEA,SAAK,UAAU,SAAS,IAAI,IAAI,iBAAiB;AACjD,UAAM,OAAO,MAAM,KAAK,UAAU,SAAS,EAAG;AAC9C,QAAI,KAAK,OAAO;AACd,YAAM,IAAI,MAAM,gBAAgB,MAAM,YAAY,KAAK,MAAM,OAAO,EAAE;AAAA,IACxE;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAEA,MAAM,WAAW,CACf,MACA,MACA,MACA,YACA,QACA,eACY;AACZ,MAAI,UAAU;AACd,MAAI,WAAW;AAEf,QAAM,OAAO,IAAI,KAAK;AACtB,OAAK,GAAG,UAAU,cAAc,MAAM;AACpC,QAAI,CAAC,UAAU;AACb,iBAAW,KAAK,SAAS,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,YAAY,MAAM;AACtB,cAAU;AAAA,EACZ;AACA,QAAM,aAAa,CAAC,WAAmB;AACrC,eAAW;AACX,eAAW,KAAK,SAAS,MAAM,MAAM;AAAA,EACvC;AAEA,QAAM,MAAM,IAAI,WAAW,MAAM,MAAM,MAAM,WAAW,YAAY,IAAI,UAAU,CAAC;AAEnF,QAAM,QAAQ,YAAY;AACxB,UAAM,qBAAqB,WAAW,MAAM;AAC1C,UAAI,EAAE,WAAW,WAAW;AAC1B,eAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,GAAK;AAGR,UAAM,uBAAuB,KAAK,YAAY;AAC5C,YAAM,EAAE,QAAQ,WAAW,IAAI,MAAM,OAAO,uBAAuB;AACnE,aAAO,OAAO;AAAA,QACZ,OAAO,SAAS;AAnHxB;AAoHU,eAAK,aAAa,WAAW,aAAa,KAAK,IAAI,EAAE;AACrD,eAAK,aAAa,WAAW,iBAAiB,KAAK,IAAI,SAAS;AAChE,eAAK,aAAa,WAAW,kBAAgB,UAAK,IAAI,SAAT,mBAAe,SAAQ,EAAE;AACtE,iBAAO,KAAK,GAAG;AAAA,QACjB;AAAA,QACA,EAAE,MAAM,iBAAiB;AAAA,MAC3B;AAAA,IACF,CAAC,EAAE,QAAQ,MAAM;AACf,mBAAa,kBAAkB;AAAA,IACjC,CAAC;AAED,UAAM,KAAK,YAAY,OAAO,EAAE,KAAK,CAAC,UAAU;AAC9C,aAAO,MAAM,eAAe;AAC5B,iBAAW;AACX,eAAS,EAAE,MAAM,WAAW,OAAO,EAAE,QAAQ,MAAM,CAAC,EAAE,EAAE,CAAC;AAAA,IAC3D,CAAC;AAGD,QAAI,IAAI,sBAAsB;AAC5B,YAAM,IAAI,qBAAqB,MAAM;AAAA,IACvC;AAGA,UAAM,IAAI,cAAc;AAExB,UAAM,KAAK,WAAW;AACtB,WAAO,MAAM,wBAAwB;AAErC,UAAM,gBAAgB,CAAC;AACvB,eAAW,YAAY,IAAI,mBAAmB;AAC5C,oBAAc,KAAK,SAAS,CAAC;AAAA,IAC/B;AACA,UAAM,QAAQ,IAAI,aAAa,EAAE;AAAA,MAAM,CAAC,UACtC,OAAO,MAAM,EAAE,MAAM,GAAG,mCAAmC;AAAA,IAC7D;AAEA,aAAS,EAAE,MAAM,QAAQ,OAAO,OAAU,CAAC;AAC3C,eAAW,QAAQ;AAAA,EACrB,GAAG;AAEH,SAAO,EAAE,KAAK,KAAK;AACrB;AAAA,CAEC,YAAY;AACX,MAAI,QAAQ,MAAM;AAChB,UAAM,OAAO,IAAI,OAAO;AAMxB,UAAM,aAAa,QAAQ,KAAK,CAAC;AACjC,UAAM,QAAe,MAAM,OAAO,cAAc,UAAW,EAAE,UAAU,KAAK,CAAC,WAAW;AAxK5F;AA0KM,YAAMA,SACJ,OAAO,OAAO,YAAY,cAAc,QAAQ,OAAO,OAAO,IAC1D,OAAO,WACP,YAAO,YAAP,mBAAgB;AACtB,UAAIA,WAAU,UAAa,CAAC,QAAQA,MAAK,GAAG;AAC1C,cAAM,IAAI,MAAM,8DAA8D,UAAU,EAAE;AAAA,MAC5F;AACA,aAAOA;AAAA,IACT,CAAC;AACD,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,UAAU;AAAA,IAClB;AAIA,YAAQ,GAAG,UAAU,MAAM;AACzB,aAAO,MAAM,6BAA6B;AAAA,IAC5C,CAAC;AAID,YAAQ,GAAG,WAAW,MAAM;AAC1B,aAAO,MAAM,8BAA8B;AAAA,IAC7C,CAAC;AAED,UAAM,KAAK,SAAS,SAAS,EAAE,KAAK,CAAC,CAAC,GAAG,MAAoB;AAC3D,YAAM;AACN,UAAI,IAAI,SAAS,qBAAqB;AACpC,cAAM,IAAI,MAAM,yCAAyC;AAAA,MAC3D;AACA,uBAAiB,IAAI,MAAM,aAAa;AAAA,IAC1C,CAAC;AACD,UAAM,OAAO,IAAI,WAAW;AAC5B,QAAI,SAAS,IAAI,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;AAE1C,YAAQ,GAAG,sBAAsB,CAAC,WAAW;AAC3C,aAAO,MAAM,EAAE,OAAO,OAAO,GAAG,6BAA6B;AAAA,IAC/D,CAAC;AAED,WAAO,MAAM,yBAAyB;AACtC,UAAM,MAAM,QAAQ,IAAI;AACxB,WAAO,MAAM,wBAAwB;AACrC,aAAS,EAAE,MAAM,sBAAsB,OAAO,OAAU,CAAC;AAEzD,QAAI,MAA2B;AAC/B,UAAM,aAAa,IAAI,aAAa;AAEpC,UAAM,kBAAkB,WAAW,MAAM;AACvC,aAAO,KAAK,sCAAsC;AAClD,WAAK,QAAQ;AAAA,IACf,GAAG,gBAAgB;AAEnB,UAAM,iBAAiB,CAAC,QAAoB;AAC1C,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK,eAAe;AAClB,0BAAgB,QAAQ;AACxB,mBAAS;AAAA,YACP,MAAM;AAAA,YACN,OAAO,EAAE,eAAe,IAAI,MAAM,WAAW,WAAW,KAAK,IAAI,EAAE;AAAA,UACrE,CAAC;AACD;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,cAAI,KAAK;AACP,kBAAM,IAAI,MAAM,0BAA0B;AAAA,UAC5C;AAEA,mBAAS,OAAO,MAAM,EAAE,OAAO,IAAI,MAAM,WAAW,IAAI,GAAG,CAAC;AAE5D,gBAAM,SAAS,MAAM,MAAM,OAAO,IAAI,MAAM,YAAY,YAAY,QAAQ,IAAI;AAChF,iBAAO,MAAM,aAAa;AAC1B;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,cAAI,CAAC,KAAK;AACR,iBAAK,QAAQ;AAAA,UACf;AACA,qBAAW,KAAK,SAAS,iBAAiB;AAC1C,uBAAa,eAAe;AAC5B,kBAAQ,IAAI,WAAW,cAAc;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,GAAG,WAAW,cAAc;AAEpC,UAAM,KAAK;AAOX,QAAI;AACF,YAAM,QAAQ;AACd,aAAO,MAAM,2BAA2B;AAAA,IAC1C,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,MAAM,GAAG,oCAAoC;AAAA,IAC7D;AAEA,WAAO,MAAM,sBAAsB;AACnC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,GAAG;","names":["agent"]}
|
|
1
|
+
{"version":3,"sources":["../../src/ipc/job_proc_lazy_main.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Room, RoomEvent, dispose } from '@livekit/rtc-node';\nimport { EventEmitter, once } from 'node:events';\nimport { pathToFileURL } from 'node:url';\nimport type { Logger } from 'pino';\nimport { type Agent, isAgent } from '../generator.js';\nimport { JobContext, JobProcess, type RunningJobInfo, runWithJobContextAsync } from '../job.js';\nimport { initializeLogger, log } from '../log.js';\nimport { Future, shortuuid } from '../utils.js';\nimport { defaultInitializeProcessFunc } from '../worker.js';\nimport type { InferenceExecutor } from './inference_executor.js';\nimport type { IPCMessage } from './message.js';\n\nconst ORPHANED_TIMEOUT = 15 * 1000;\n\nconst safeSend = (msg: IPCMessage): boolean => {\n try {\n if (process.connected && process.send) {\n process.send(msg);\n return true;\n }\n return false;\n } catch (error) {\n // Channel closed is expected during graceful shutdown\n // Log at debug level to avoid noise in production logs\n if (error instanceof Error && error.message.includes('Channel closed')) {\n log().debug({ msgCase: msg.case }, 'IPC channel closed, message not sent');\n } else {\n log().error({ error, msgCase: msg.case }, 'IPC send failed unexpectedly');\n }\n return false;\n }\n};\n\ntype JobTask = {\n ctx: JobContext;\n task: Promise<void>;\n};\n\nclass PendingInference {\n promise = new Promise<{ requestId: string; data: unknown; error?: Error }>((resolve) => {\n this.resolve = resolve; // this is how JavaScript lets you resolve promises externally\n });\n resolve(arg: { requestId: string; data: unknown; error?: Error }) {\n arg; // useless call to counteract TypeScript E6133\n }\n}\n\nclass InfClient implements InferenceExecutor {\n #requests: { [id: string]: PendingInference } = {};\n\n constructor() {\n process.on('message', (msg: IPCMessage) => {\n switch (msg.case) {\n case 'inferenceResponse':\n const fut = this.#requests[msg.value.requestId];\n delete this.#requests[msg.value.requestId];\n if (!fut) {\n log().child({ resp: msg.value }).warn('received unexpected inference response');\n return;\n }\n fut.resolve(msg.value);\n break;\n }\n });\n }\n\n async doInference(method: string, data: unknown): Promise<unknown> {\n const requestId = shortuuid('inference_job_');\n if (!safeSend({ case: 'inferenceRequest', value: { requestId, method, data } })) {\n throw new Error('IPC channel closed');\n }\n\n this.#requests[requestId] = new PendingInference();\n const resp = await this.#requests[requestId]!.promise;\n if (resp.error) {\n throw new Error(`inference of ${method} failed: ${resp.error.message}`);\n }\n return resp.data;\n }\n}\n\nconst startJob = (\n proc: JobProcess,\n func: (ctx: JobContext) => Promise<void>,\n info: RunningJobInfo,\n closeEvent: EventEmitter,\n logger: Logger,\n joinFuture: Future,\n): JobTask => {\n let connect = false;\n let shutdown = false;\n\n const room = new Room();\n room.on(RoomEvent.Disconnected, () => {\n if (!shutdown) {\n closeEvent.emit('close', false);\n }\n });\n\n const onConnect = () => {\n connect = true;\n };\n const onShutdown = (reason: string) => {\n shutdown = true;\n closeEvent.emit('close', true, reason);\n };\n\n const ctx = new JobContext(proc, info, room, onConnect, onShutdown, new InfClient());\n\n const task = (async () => {\n const unconnectedTimeout = setTimeout(() => {\n if (!(connect || shutdown)) {\n logger.warn(\n 'room not connect after job_entry was called after 10 seconds, ',\n 'did you forget to call ctx.connect()?',\n );\n }\n }, 10000);\n\n try {\n // Run the job function within the AsyncLocalStorage context\n await runWithJobContextAsync(ctx, async () => {\n const { tracer, traceTypes } = await import('../telemetry/index.js');\n return tracer.startActiveSpan(\n async (span) => {\n span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);\n span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);\n span.setAttribute(traceTypes.ATTR_ROOM_NAME, info.job.room?.name ?? '');\n return func(ctx);\n },\n { name: 'job_entrypoint' },\n );\n }).finally(() => {\n clearTimeout(unconnectedTimeout);\n });\n\n await once(closeEvent, 'close').then((close) => {\n logger.debug('shutting down');\n shutdown = true;\n safeSend({ case: 'exiting', value: { reason: close[1] } });\n });\n } catch (error) {\n logger.error({ error }, 'error in entry function');\n shutdown = true;\n safeSend({\n case: 'exiting',\n value: { reason: error instanceof Error ? error.message : String(error) },\n });\n }\n\n // Close the primary agent session if it exists\n if (ctx._primaryAgentSession) {\n await ctx._primaryAgentSession.close();\n }\n\n // Generate and save/upload session report\n await ctx._onSessionEnd();\n\n await room.disconnect();\n logger.debug('disconnected from room');\n\n const shutdownTasks = [];\n for (const callback of ctx.shutdownCallbacks) {\n shutdownTasks.push(callback());\n }\n await Promise.all(shutdownTasks).catch((error) =>\n logger.error({ error }, 'error while shutting down the job'),\n );\n\n safeSend({ case: 'done', value: undefined });\n joinFuture.resolve();\n })();\n\n return { ctx, task };\n};\n\n(async () => {\n if (process.send) {\n const join = new Future();\n\n // process.argv:\n // [0] `node'\n // [1] import.meta.filename\n // [2] import.meta.filename of function containing entry file\n const moduleFile = process.argv[2];\n const agent: Agent = await import(pathToFileURL(moduleFile!).pathname).then((module) => {\n // Handle both ESM (module.default is the agent) and CJS (module.default.default is the agent)\n const agent =\n typeof module.default === 'function' || isAgent(module.default)\n ? module.default\n : module.default?.default;\n if (agent === undefined || !isAgent(agent)) {\n throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`);\n }\n return agent;\n });\n if (!agent.prewarm) {\n agent.prewarm = defaultInitializeProcessFunc;\n }\n\n // don't do anything on C-c\n // this is handled in cli, triggering a termination of all child processes at once.\n process.on('SIGINT', () => {\n logger.debug('SIGINT received in job proc');\n });\n\n // don't do anything on SIGTERM\n // Render uses SIGTERM in autoscale, this ensures the processes are properly drained if needed\n process.on('SIGTERM', () => {\n logger.debug('SIGTERM received in job proc');\n });\n\n await once(process, 'message').then(([msg]: IPCMessage[]) => {\n msg = msg!;\n if (msg.case !== 'initializeRequest') {\n throw new Error('first message must be InitializeRequest');\n }\n initializeLogger(msg.value.loggerOptions);\n });\n const proc = new JobProcess();\n let logger = log().child({ pid: proc.pid });\n\n process.on('unhandledRejection', (reason) => {\n logger.debug({ error: reason }, 'Unhandled promise rejection');\n });\n\n logger.debug('initializing job runner');\n await agent.prewarm(proc);\n logger.debug('job runner initialized');\n safeSend({ case: 'initializeResponse', value: undefined });\n\n let job: JobTask | undefined = undefined;\n const closeEvent = new EventEmitter();\n\n const orphanedTimeout = setTimeout(() => {\n logger.warn('job process orphaned, shutting down.');\n join.resolve();\n }, ORPHANED_TIMEOUT);\n\n const messageHandler = (msg: IPCMessage) => {\n switch (msg.case) {\n case 'pingRequest': {\n orphanedTimeout.refresh();\n safeSend({\n case: 'pongResponse',\n value: { lastTimestamp: msg.value.timestamp, timestamp: Date.now() },\n });\n break;\n }\n case 'startJobRequest': {\n if (job) {\n throw new Error('job task already running');\n }\n\n logger = logger.child({ jobID: msg.value.runningJob.job.id });\n\n job = startJob(proc, agent.entry, msg.value.runningJob, closeEvent, logger, join);\n logger.debug('job started');\n break;\n }\n case 'shutdownRequest': {\n if (!job) {\n join.resolve();\n }\n closeEvent.emit('close', 'shutdownRequest');\n clearTimeout(orphanedTimeout);\n process.off('message', messageHandler);\n }\n }\n };\n\n process.on('message', messageHandler);\n\n await join.await;\n\n // Dispose native FFI resources (Rust FfiServer, tokio runtimes, libwebrtc)\n // before process.exit() to prevent libc++abi mutex crash during teardown.\n // Without this, process.exit() can kill the process while native threads are\n // still running, causing: \"mutex lock failed: Invalid argument\"\n // See: https://github.com/livekit/node-sdks/issues/564\n try {\n await dispose();\n logger.debug('native resources disposed');\n } catch (error) {\n logger.warn({ error }, 'failed to dispose native resources');\n }\n\n logger.debug('Job process shutdown');\n process.exit(0);\n }\n})();\n"],"mappings":"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","names":["agent"]}
|
package/dist/job.cjs
CHANGED
|
@@ -216,7 +216,7 @@ class JobContext {
|
|
|
216
216
|
jobId: this.job.id,
|
|
217
217
|
roomId: ((_a = this.job.room) == null ? void 0 : _a.sid) || "",
|
|
218
218
|
room: ((_b = this.job.room) == null ? void 0 : _b.name) || "",
|
|
219
|
-
options: targetSession.
|
|
219
|
+
options: targetSession.sessionOptions,
|
|
220
220
|
events: targetSession._recordedEvents,
|
|
221
221
|
enableRecording: targetSession._enableRecording,
|
|
222
222
|
chatHistory: targetSession.history.copy(),
|
package/dist/job.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/job.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as proto from '@livekit/protocol';\nimport type {\n E2EEOptions,\n LocalParticipant,\n RemoteParticipant,\n Room,\n RtcConfiguration,\n} from '@livekit/rtc-node';\nimport { ParticipantKind, RoomEvent, TrackKind } from '@livekit/rtc-node';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { Logger } from 'pino';\nimport type { InferenceExecutor } from './ipc/inference_executor.js';\nimport { log } from './log.js';\nimport { flushOtelLogs, setupCloudTracer, uploadSessionReport } from './telemetry/index.js';\nimport { isCloud } from './utils.js';\nimport type { AgentSession } from './voice/agent_session.js';\nimport { type SessionReport, createSessionReport } from './voice/report.js';\n\n// AsyncLocalStorage for job context, similar to Python's contextvars\nconst jobContextStorage = new AsyncLocalStorage<JobContext>();\n\n/**\n * Returns the current job context.\n *\n * @throws {Error} if no job context is found\n */\nexport function getJobContext(): JobContext {\n const ctx = jobContextStorage.getStore();\n if (!ctx) {\n throw new Error('no job context found, are you running this code inside a job entrypoint?');\n }\n return ctx;\n}\n\n/**\n * Runs a function within a job context, similar to Python's contextvars.\n * @internal\n */\nexport function runWithJobContext<T>(context: JobContext, fn: () => T): T {\n return jobContextStorage.run(context, fn);\n}\n\n/**\n * Runs an async function within a job context, similar to Python's contextvars.\n * @internal\n */\nexport function runWithJobContextAsync<T>(context: JobContext, fn: () => Promise<T>): Promise<T> {\n return jobContextStorage.run(context, fn);\n}\n\n/** Which tracks, if any, should the agent automatically subscribe to? */\nexport enum AutoSubscribe {\n SUBSCRIBE_ALL,\n SUBSCRIBE_NONE,\n VIDEO_ONLY,\n AUDIO_ONLY,\n}\n\nexport type JobAcceptArguments = {\n name: string;\n identity: string;\n metadata: string;\n attributes?: { [key: string]: string };\n};\n\nexport type RunningJobInfo = {\n acceptArguments: JobAcceptArguments;\n job: proto.Job;\n url: string;\n token: string;\n workerId: string;\n};\n\n/** Attempted to add a function callback, but the function already exists. */\nexport class FunctionExistsError extends Error {\n constructor(msg?: string) {\n super(msg);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** The job and environment context as seen by the agent, accessible by the entrypoint function. */\nexport class JobContext {\n #proc: JobProcess;\n #info: RunningJobInfo;\n #room: Room;\n #onConnect: () => void;\n #onShutdown: (s: string) => void;\n /** @internal */\n shutdownCallbacks: (() => Promise<void>)[] = [];\n #participantEntrypoints: ((job: JobContext, p: RemoteParticipant) => Promise<void>)[] = [];\n #participantTasks: {\n [id: string]: {\n callback: (job: JobContext, p: RemoteParticipant) => Promise<void>;\n result: Promise<void>;\n };\n } = {};\n #logger: Logger;\n #inferenceExecutor: InferenceExecutor;\n\n /** @internal */\n _primaryAgentSession?: AgentSession;\n\n /** @internal */\n _sessionDirectory: string;\n\n private connected: boolean = false;\n\n constructor(\n proc: JobProcess,\n info: RunningJobInfo,\n room: Room,\n onConnect: () => void,\n onShutdown: (s: string) => void,\n inferenceExecutor: InferenceExecutor,\n ) {\n this.#proc = proc;\n this.#info = info;\n this.#room = room;\n this.#onConnect = onConnect;\n this.#onShutdown = onShutdown;\n this.onParticipantConnected = this.onParticipantConnected.bind(this);\n this.#room.on(RoomEvent.ParticipantConnected, this.onParticipantConnected);\n this.#logger = log().child({\n jobId: this.#info.job.id,\n roomName: this.#info.job.room?.name,\n });\n this.#inferenceExecutor = inferenceExecutor;\n this._sessionDirectory = path.join(os.tmpdir(), 'livekit-agents', `job-${this.#info.job.id}`);\n }\n\n get proc(): JobProcess {\n return this.#proc;\n }\n\n get job(): proto.Job {\n return this.#info.job;\n }\n\n get workerId(): string {\n return this.#info.workerId;\n }\n\n /** @returns The room the agent was called into */\n get room(): Room {\n return this.#room;\n }\n\n get info(): RunningJobInfo {\n return this.#info;\n }\n\n /** @returns The agent's participant if connected to the room, otherwise `undefined` */\n get agent(): LocalParticipant | undefined {\n return this.#room.localParticipant;\n }\n\n /** @returns The global inference executor */\n get inferenceExecutor(): InferenceExecutor {\n return this.#inferenceExecutor;\n }\n\n /**\n * @returns The session directory for storing recordings and session data.\n */\n get sessionDirectory(): string {\n return this._sessionDirectory;\n }\n\n /** Adds a promise to be awaited when {@link JobContext.shutdown | shutdown} is called. */\n addShutdownCallback(callback: () => Promise<void>) {\n this.shutdownCallbacks.push(callback);\n }\n\n async waitForParticipant(identity?: string): Promise<RemoteParticipant> {\n if (!this.#room.isConnected) {\n throw new Error('room is not connected');\n }\n\n for (const p of this.#room.remoteParticipants.values()) {\n if ((!identity || p.identity === identity) && p.info.kind != ParticipantKind.AGENT) {\n return p;\n }\n }\n\n return new Promise((resolve, reject) => {\n const onParticipantConnected = (participant: RemoteParticipant) => {\n if (\n (!identity || participant.identity === identity) &&\n participant.info.kind != ParticipantKind.AGENT\n ) {\n clearHandlers();\n resolve(participant);\n }\n };\n const onDisconnected = () => {\n clearHandlers();\n reject(new Error('Room disconnected while waiting for participant'));\n };\n\n const clearHandlers = () => {\n this.#room.off(RoomEvent.ParticipantConnected, onParticipantConnected);\n this.#room.off(RoomEvent.Disconnected, onDisconnected);\n };\n\n this.#room.on(RoomEvent.ParticipantConnected, onParticipantConnected);\n this.#room.on(RoomEvent.Disconnected, onDisconnected);\n });\n }\n\n /**\n * Connects the agent to the room.\n *\n * @remarks\n * It is recommended to run this command as early in the function as possible, as executing it\n * later may cause noticeable delay between user and agent joins.\n *\n * @see {@link https://github.com/livekit/node-sdks/tree/main/packages/livekit-rtc#readme |\n * @livekit/rtc-node} for more information about the parameters.\n */\n async connect(\n e2ee?: E2EEOptions,\n autoSubscribe: AutoSubscribe = AutoSubscribe.SUBSCRIBE_ALL,\n rtcConfig?: RtcConfiguration,\n ) {\n if (this.connected) {\n return;\n }\n\n const opts = {\n e2ee,\n autoSubscribe: autoSubscribe == AutoSubscribe.SUBSCRIBE_ALL,\n rtcConfig,\n dynacast: false,\n };\n\n await this.#room.connect(this.#info.url, this.#info.token, opts);\n this.#onConnect();\n\n this.#room.remoteParticipants.forEach(this.onParticipantConnected);\n\n if ([AutoSubscribe.AUDIO_ONLY, AutoSubscribe.VIDEO_ONLY].includes(autoSubscribe)) {\n this.#room.remoteParticipants.forEach((p) => {\n p.trackPublications.forEach((pub) => {\n if (\n (autoSubscribe === AutoSubscribe.AUDIO_ONLY && pub.kind === TrackKind.KIND_AUDIO) ||\n (autoSubscribe === AutoSubscribe.VIDEO_ONLY && pub.kind === TrackKind.KIND_VIDEO)\n ) {\n pub.setSubscribed(true);\n }\n });\n });\n }\n this.connected = true;\n }\n\n makeSessionReport(session?: AgentSession): SessionReport {\n const targetSession = session || this._primaryAgentSession;\n\n if (!targetSession) {\n throw new Error('Cannot prepare report, no AgentSession was found');\n }\n\n const recorderIO = targetSession._recorderIO;\n\n if (recorderIO && recorderIO.recording) {\n throw new Error('Cannot create the AgentSession report, the RecorderIO is still recording');\n }\n\n return createSessionReport({\n jobId: this.job.id,\n roomId: this.job.room?.sid || '',\n room: this.job.room?.name || '',\n options: targetSession.options,\n events: targetSession._recordedEvents,\n enableRecording: targetSession._enableRecording,\n chatHistory: targetSession.history.copy(),\n startedAt: targetSession._startedAt,\n audioRecordingPath: recorderIO?.outputPath,\n audioRecordingStartedAt: recorderIO?.recordingStartedAt,\n });\n }\n\n async _onSessionEnd(): Promise<void> {\n const session = this._primaryAgentSession;\n if (!session) {\n return;\n }\n\n const report = this.makeSessionReport(session);\n\n // TODO(brian): Implement CLI/console\n\n // Upload session report to LiveKit Cloud if enabled\n const url = new URL(this.#info.url);\n\n if (report.enableRecording && isCloud(url)) {\n try {\n await uploadSessionReport({\n agentName: this.job.agentName,\n cloudHostname: url.hostname,\n report,\n });\n this.#logger.info(\n {\n jobId: report.jobId,\n roomId: report.roomId,\n },\n 'Session report uploaded to LiveKit Cloud',\n );\n } catch (error) {\n this.#logger.error({ error }, 'Failed to upload session report');\n }\n }\n\n this.#logger.debug(\n {\n jobId: report.jobId,\n roomId: report.roomId,\n eventsCount: report.events.length,\n },\n 'Session ended, report generated',\n );\n\n // Explicitly clear the recorded events to avoid leaking memory\n session._recordedEvents = [];\n\n try {\n await flushOtelLogs();\n } catch (error) {\n this.#logger.error({ error }, 'Failed to flush OTEL logs');\n }\n }\n\n /**\n * Gracefully shuts down the job, and runs all shutdown promises.\n *\n * @param reason - Optional reason for shutdown\n */\n shutdown(reason = '') {\n this.#onShutdown(reason);\n }\n\n /** @internal */\n onParticipantConnected(p: RemoteParticipant) {\n for (const callback of this.#participantEntrypoints) {\n if (this.#participantTasks[p.identity!]?.callback == callback) {\n this.#logger.warn(\n 'a participant has joined before a prior prticipant task matching the same identity has finished:',\n p.identity,\n );\n }\n const result = callback(this, p);\n result.finally(() => delete this.#participantTasks[p.identity!]);\n this.#participantTasks[p.identity!] = { callback, result };\n }\n }\n\n /**\n * Adds a promise to be awaited whenever a new participant joins the room.\n *\n * @throws {@link FunctionExistsError} if an entrypoint already exists\n */\n addParticipantEntrypoint(callback: (job: JobContext, p: RemoteParticipant) => Promise<void>) {\n if (this.#participantEntrypoints.includes(callback)) {\n throw new FunctionExistsError('entrypoints cannot be added more than once');\n }\n\n this.#participantEntrypoints.push(callback);\n }\n\n async initRecording() {\n const url = new URL(this.#info.url);\n if (!isCloud(url)) {\n return;\n }\n\n this.#logger.debug({ hostname: url.hostname }, 'Configuring session recording (cloud tracer)');\n await setupCloudTracer({\n roomId: this.job.room!.sid,\n jobId: this.job.id,\n cloudHostname: url.hostname,\n });\n }\n}\n\nexport class JobProcess {\n #pid = process.pid;\n userData: { [id: string]: unknown } = {};\n\n get pid(): number {\n return this.#pid;\n }\n}\n\n/**\n * A request sent by the server to spawn a new agent job.\n *\n * @remarks\n * For most applications, this is best left to the default, which simply accepts the job and\n * handles the logic inside the entrypoint function. This class is useful for vetting which\n * requests should fill idle processes and which should be outright rejected.\n */\nexport class JobRequest {\n #job: proto.Job;\n #onReject: () => Promise<void>;\n #onAccept: (args: JobAcceptArguments) => Promise<void>;\n\n /** @internal */\n constructor(\n job: proto.Job,\n onReject: () => Promise<void>,\n onAccept: (args: JobAcceptArguments) => Promise<void>,\n ) {\n this.#job = job;\n this.#onReject = onReject;\n this.#onAccept = onAccept;\n }\n\n /** @returns The ID of the job, set by the LiveKit server */\n get id(): string {\n return this.#job.id;\n }\n\n /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n get job(): proto.Job {\n return this.#job;\n }\n\n /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n get room(): proto.Room | undefined {\n return this.#job.room;\n }\n\n /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n get publisher(): proto.ParticipantInfo | undefined {\n return this.#job.participant;\n }\n\n /** @returns The agent's name, as set in {@link WorkerOptions} */\n get agentName(): string {\n return this.#job.agentName;\n }\n\n /** Rejects the job. */\n async reject() {\n await this.#onReject();\n }\n\n /** Accepts the job, launching it on an idle child process. */\n async accept(name = '', identity = '', metadata = '', attributes?: { [key: string]: string }) {\n if (identity === '') identity = 'agent-' + this.id;\n\n this.#onAccept({ name, identity, metadata, attributes });\n }\n}\n"],"mappings":"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","names":["AutoSubscribe"]}
|
|
1
|
+
{"version":3,"sources":["../src/job.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as proto from '@livekit/protocol';\nimport type {\n E2EEOptions,\n LocalParticipant,\n RemoteParticipant,\n Room,\n RtcConfiguration,\n} from '@livekit/rtc-node';\nimport { ParticipantKind, RoomEvent, TrackKind } from '@livekit/rtc-node';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { Logger } from 'pino';\nimport type { InferenceExecutor } from './ipc/inference_executor.js';\nimport { log } from './log.js';\nimport { flushOtelLogs, setupCloudTracer, uploadSessionReport } from './telemetry/index.js';\nimport { isCloud } from './utils.js';\nimport type { AgentSession } from './voice/agent_session.js';\nimport { type SessionReport, createSessionReport } from './voice/report.js';\n\n// AsyncLocalStorage for job context, similar to Python's contextvars\nconst jobContextStorage = new AsyncLocalStorage<JobContext>();\n\n/**\n * Returns the current job context.\n *\n * @throws {Error} if no job context is found\n */\nexport function getJobContext(): JobContext {\n const ctx = jobContextStorage.getStore();\n if (!ctx) {\n throw new Error('no job context found, are you running this code inside a job entrypoint?');\n }\n return ctx;\n}\n\n/**\n * Runs a function within a job context, similar to Python's contextvars.\n * @internal\n */\nexport function runWithJobContext<T>(context: JobContext, fn: () => T): T {\n return jobContextStorage.run(context, fn);\n}\n\n/**\n * Runs an async function within a job context, similar to Python's contextvars.\n * @internal\n */\nexport function runWithJobContextAsync<T>(context: JobContext, fn: () => Promise<T>): Promise<T> {\n return jobContextStorage.run(context, fn);\n}\n\n/** Which tracks, if any, should the agent automatically subscribe to? */\nexport enum AutoSubscribe {\n SUBSCRIBE_ALL,\n SUBSCRIBE_NONE,\n VIDEO_ONLY,\n AUDIO_ONLY,\n}\n\nexport type JobAcceptArguments = {\n name: string;\n identity: string;\n metadata: string;\n attributes?: { [key: string]: string };\n};\n\nexport type RunningJobInfo = {\n acceptArguments: JobAcceptArguments;\n job: proto.Job;\n url: string;\n token: string;\n workerId: string;\n};\n\n/** Attempted to add a function callback, but the function already exists. */\nexport class FunctionExistsError extends Error {\n constructor(msg?: string) {\n super(msg);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** The job and environment context as seen by the agent, accessible by the entrypoint function. */\nexport class JobContext {\n #proc: JobProcess;\n #info: RunningJobInfo;\n #room: Room;\n #onConnect: () => void;\n #onShutdown: (s: string) => void;\n /** @internal */\n shutdownCallbacks: (() => Promise<void>)[] = [];\n #participantEntrypoints: ((job: JobContext, p: RemoteParticipant) => Promise<void>)[] = [];\n #participantTasks: {\n [id: string]: {\n callback: (job: JobContext, p: RemoteParticipant) => Promise<void>;\n result: Promise<void>;\n };\n } = {};\n #logger: Logger;\n #inferenceExecutor: InferenceExecutor;\n\n /** @internal */\n _primaryAgentSession?: AgentSession;\n\n /** @internal */\n _sessionDirectory: string;\n\n private connected: boolean = false;\n\n constructor(\n proc: JobProcess,\n info: RunningJobInfo,\n room: Room,\n onConnect: () => void,\n onShutdown: (s: string) => void,\n inferenceExecutor: InferenceExecutor,\n ) {\n this.#proc = proc;\n this.#info = info;\n this.#room = room;\n this.#onConnect = onConnect;\n this.#onShutdown = onShutdown;\n this.onParticipantConnected = this.onParticipantConnected.bind(this);\n this.#room.on(RoomEvent.ParticipantConnected, this.onParticipantConnected);\n this.#logger = log().child({\n jobId: this.#info.job.id,\n roomName: this.#info.job.room?.name,\n });\n this.#inferenceExecutor = inferenceExecutor;\n this._sessionDirectory = path.join(os.tmpdir(), 'livekit-agents', `job-${this.#info.job.id}`);\n }\n\n get proc(): JobProcess {\n return this.#proc;\n }\n\n get job(): proto.Job {\n return this.#info.job;\n }\n\n get workerId(): string {\n return this.#info.workerId;\n }\n\n /** @returns The room the agent was called into */\n get room(): Room {\n return this.#room;\n }\n\n get info(): RunningJobInfo {\n return this.#info;\n }\n\n /** @returns The agent's participant if connected to the room, otherwise `undefined` */\n get agent(): LocalParticipant | undefined {\n return this.#room.localParticipant;\n }\n\n /** @returns The global inference executor */\n get inferenceExecutor(): InferenceExecutor {\n return this.#inferenceExecutor;\n }\n\n /**\n * @returns The session directory for storing recordings and session data.\n */\n get sessionDirectory(): string {\n return this._sessionDirectory;\n }\n\n /** Adds a promise to be awaited when {@link JobContext.shutdown | shutdown} is called. */\n addShutdownCallback(callback: () => Promise<void>) {\n this.shutdownCallbacks.push(callback);\n }\n\n async waitForParticipant(identity?: string): Promise<RemoteParticipant> {\n if (!this.#room.isConnected) {\n throw new Error('room is not connected');\n }\n\n for (const p of this.#room.remoteParticipants.values()) {\n if ((!identity || p.identity === identity) && p.info.kind != ParticipantKind.AGENT) {\n return p;\n }\n }\n\n return new Promise((resolve, reject) => {\n const onParticipantConnected = (participant: RemoteParticipant) => {\n if (\n (!identity || participant.identity === identity) &&\n participant.info.kind != ParticipantKind.AGENT\n ) {\n clearHandlers();\n resolve(participant);\n }\n };\n const onDisconnected = () => {\n clearHandlers();\n reject(new Error('Room disconnected while waiting for participant'));\n };\n\n const clearHandlers = () => {\n this.#room.off(RoomEvent.ParticipantConnected, onParticipantConnected);\n this.#room.off(RoomEvent.Disconnected, onDisconnected);\n };\n\n this.#room.on(RoomEvent.ParticipantConnected, onParticipantConnected);\n this.#room.on(RoomEvent.Disconnected, onDisconnected);\n });\n }\n\n /**\n * Connects the agent to the room.\n *\n * @remarks\n * It is recommended to run this command as early in the function as possible, as executing it\n * later may cause noticeable delay between user and agent joins.\n *\n * @see {@link https://github.com/livekit/node-sdks/tree/main/packages/livekit-rtc#readme |\n * @livekit/rtc-node} for more information about the parameters.\n */\n async connect(\n e2ee?: E2EEOptions,\n autoSubscribe: AutoSubscribe = AutoSubscribe.SUBSCRIBE_ALL,\n rtcConfig?: RtcConfiguration,\n ) {\n if (this.connected) {\n return;\n }\n\n const opts = {\n e2ee,\n autoSubscribe: autoSubscribe == AutoSubscribe.SUBSCRIBE_ALL,\n rtcConfig,\n dynacast: false,\n };\n\n await this.#room.connect(this.#info.url, this.#info.token, opts);\n this.#onConnect();\n\n this.#room.remoteParticipants.forEach(this.onParticipantConnected);\n\n if ([AutoSubscribe.AUDIO_ONLY, AutoSubscribe.VIDEO_ONLY].includes(autoSubscribe)) {\n this.#room.remoteParticipants.forEach((p) => {\n p.trackPublications.forEach((pub) => {\n if (\n (autoSubscribe === AutoSubscribe.AUDIO_ONLY && pub.kind === TrackKind.KIND_AUDIO) ||\n (autoSubscribe === AutoSubscribe.VIDEO_ONLY && pub.kind === TrackKind.KIND_VIDEO)\n ) {\n pub.setSubscribed(true);\n }\n });\n });\n }\n this.connected = true;\n }\n\n makeSessionReport(session?: AgentSession): SessionReport {\n const targetSession = session || this._primaryAgentSession;\n\n if (!targetSession) {\n throw new Error('Cannot prepare report, no AgentSession was found');\n }\n\n const recorderIO = targetSession._recorderIO;\n\n if (recorderIO && recorderIO.recording) {\n throw new Error('Cannot create the AgentSession report, the RecorderIO is still recording');\n }\n\n return createSessionReport({\n jobId: this.job.id,\n roomId: this.job.room?.sid || '',\n room: this.job.room?.name || '',\n options: targetSession.sessionOptions,\n events: targetSession._recordedEvents,\n enableRecording: targetSession._enableRecording,\n chatHistory: targetSession.history.copy(),\n startedAt: targetSession._startedAt,\n audioRecordingPath: recorderIO?.outputPath,\n audioRecordingStartedAt: recorderIO?.recordingStartedAt,\n });\n }\n\n async _onSessionEnd(): Promise<void> {\n const session = this._primaryAgentSession;\n if (!session) {\n return;\n }\n\n const report = this.makeSessionReport(session);\n\n // TODO(brian): Implement CLI/console\n\n // Upload session report to LiveKit Cloud if enabled\n const url = new URL(this.#info.url);\n\n if (report.enableRecording && isCloud(url)) {\n try {\n await uploadSessionReport({\n agentName: this.job.agentName,\n cloudHostname: url.hostname,\n report,\n });\n this.#logger.info(\n {\n jobId: report.jobId,\n roomId: report.roomId,\n },\n 'Session report uploaded to LiveKit Cloud',\n );\n } catch (error) {\n this.#logger.error({ error }, 'Failed to upload session report');\n }\n }\n\n this.#logger.debug(\n {\n jobId: report.jobId,\n roomId: report.roomId,\n eventsCount: report.events.length,\n },\n 'Session ended, report generated',\n );\n\n // Explicitly clear the recorded events to avoid leaking memory\n session._recordedEvents = [];\n\n try {\n await flushOtelLogs();\n } catch (error) {\n this.#logger.error({ error }, 'Failed to flush OTEL logs');\n }\n }\n\n /**\n * Gracefully shuts down the job, and runs all shutdown promises.\n *\n * @param reason - Optional reason for shutdown\n */\n shutdown(reason = '') {\n this.#onShutdown(reason);\n }\n\n /** @internal */\n onParticipantConnected(p: RemoteParticipant) {\n for (const callback of this.#participantEntrypoints) {\n if (this.#participantTasks[p.identity!]?.callback == callback) {\n this.#logger.warn(\n 'a participant has joined before a prior prticipant task matching the same identity has finished:',\n p.identity,\n );\n }\n const result = callback(this, p);\n result.finally(() => delete this.#participantTasks[p.identity!]);\n this.#participantTasks[p.identity!] = { callback, result };\n }\n }\n\n /**\n * Adds a promise to be awaited whenever a new participant joins the room.\n *\n * @throws {@link FunctionExistsError} if an entrypoint already exists\n */\n addParticipantEntrypoint(callback: (job: JobContext, p: RemoteParticipant) => Promise<void>) {\n if (this.#participantEntrypoints.includes(callback)) {\n throw new FunctionExistsError('entrypoints cannot be added more than once');\n }\n\n this.#participantEntrypoints.push(callback);\n }\n\n async initRecording() {\n const url = new URL(this.#info.url);\n if (!isCloud(url)) {\n return;\n }\n\n this.#logger.debug({ hostname: url.hostname }, 'Configuring session recording (cloud tracer)');\n await setupCloudTracer({\n roomId: this.job.room!.sid,\n jobId: this.job.id,\n cloudHostname: url.hostname,\n });\n }\n}\n\nexport class JobProcess {\n #pid = process.pid;\n userData: { [id: string]: unknown } = {};\n\n get pid(): number {\n return this.#pid;\n }\n}\n\n/**\n * A request sent by the server to spawn a new agent job.\n *\n * @remarks\n * For most applications, this is best left to the default, which simply accepts the job and\n * handles the logic inside the entrypoint function. This class is useful for vetting which\n * requests should fill idle processes and which should be outright rejected.\n */\nexport class JobRequest {\n #job: proto.Job;\n #onReject: () => Promise<void>;\n #onAccept: (args: JobAcceptArguments) => Promise<void>;\n\n /** @internal */\n constructor(\n job: proto.Job,\n onReject: () => Promise<void>,\n onAccept: (args: JobAcceptArguments) => Promise<void>,\n ) {\n this.#job = job;\n this.#onReject = onReject;\n this.#onAccept = onAccept;\n }\n\n /** @returns The ID of the job, set by the LiveKit server */\n get id(): string {\n return this.#job.id;\n }\n\n /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n get job(): proto.Job {\n return this.#job;\n }\n\n /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n get room(): proto.Room | undefined {\n return this.#job.room;\n }\n\n /** @see {@link https://www.npmjs.com/package/@livekit/protocol | @livekit/protocol} */\n get publisher(): proto.ParticipantInfo | undefined {\n return this.#job.participant;\n }\n\n /** @returns The agent's name, as set in {@link WorkerOptions} */\n get agentName(): string {\n return this.#job.agentName;\n }\n\n /** Rejects the job. */\n async reject() {\n await this.#onReject();\n }\n\n /** Accepts the job, launching it on an idle child process. */\n async accept(name = '', identity = '', metadata = '', attributes?: { [key: string]: string }) {\n if (identity === '') identity = 'agent-' + this.id;\n\n this.#onAccept({ name, identity, metadata, attributes });\n }\n}\n"],"mappings":"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","names":["AutoSubscribe"]}
|
package/dist/job.js
CHANGED
|
@@ -176,7 +176,7 @@ class JobContext {
|
|
|
176
176
|
jobId: this.job.id,
|
|
177
177
|
roomId: ((_a = this.job.room) == null ? void 0 : _a.sid) || "",
|
|
178
178
|
room: ((_b = this.job.room) == null ? void 0 : _b.name) || "",
|
|
179
|
-
options: targetSession.
|
|
179
|
+
options: targetSession.sessionOptions,
|
|
180
180
|
events: targetSession._recordedEvents,
|
|
181
181
|
enableRecording: targetSession._enableRecording,
|
|
182
182
|
chatHistory: targetSession.history.copy(),
|