@livekit/agents-plugin-livekit 0.1.3 → 1.0.0-next.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.
Files changed (91) hide show
  1. package/dist/hf_utils.cjs +272 -0
  2. package/dist/hf_utils.cjs.map +1 -0
  3. package/dist/hf_utils.d.cts +40 -0
  4. package/dist/hf_utils.d.ts +40 -0
  5. package/dist/hf_utils.d.ts.map +1 -0
  6. package/dist/hf_utils.js +237 -0
  7. package/dist/hf_utils.js.map +1 -0
  8. package/dist/hf_utils.test.cjs +330 -0
  9. package/dist/hf_utils.test.cjs.map +1 -0
  10. package/dist/hf_utils.test.d.cts +2 -0
  11. package/dist/hf_utils.test.d.ts +2 -0
  12. package/dist/hf_utils.test.d.ts.map +1 -0
  13. package/dist/hf_utils.test.js +307 -0
  14. package/dist/hf_utils.test.js.map +1 -0
  15. package/dist/index.cjs +27 -10
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +2 -2
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +24 -6
  21. package/dist/index.js.map +1 -1
  22. package/dist/turn_detector/base.cjs +202 -0
  23. package/dist/turn_detector/base.cjs.map +1 -0
  24. package/dist/turn_detector/base.d.cts +52 -0
  25. package/dist/turn_detector/base.d.ts +52 -0
  26. package/dist/turn_detector/base.d.ts.map +1 -0
  27. package/dist/turn_detector/base.js +172 -0
  28. package/dist/turn_detector/base.js.map +1 -0
  29. package/dist/turn_detector/constants.cjs +44 -0
  30. package/dist/turn_detector/constants.cjs.map +1 -0
  31. package/dist/turn_detector/constants.d.cts +7 -0
  32. package/dist/turn_detector/constants.d.ts +7 -0
  33. package/dist/turn_detector/constants.d.ts.map +1 -0
  34. package/dist/turn_detector/constants.js +16 -0
  35. package/dist/turn_detector/constants.js.map +1 -0
  36. package/dist/turn_detector/english.cjs +52 -0
  37. package/dist/turn_detector/english.cjs.map +1 -0
  38. package/dist/turn_detector/english.d.cts +11 -0
  39. package/dist/turn_detector/english.d.ts +11 -0
  40. package/dist/turn_detector/english.d.ts.map +1 -0
  41. package/dist/turn_detector/english.js +26 -0
  42. package/dist/turn_detector/english.js.map +1 -0
  43. package/dist/turn_detector/index.cjs +53 -0
  44. package/dist/turn_detector/index.cjs.map +1 -0
  45. package/dist/turn_detector/index.d.cts +5 -0
  46. package/dist/turn_detector/index.d.ts +5 -0
  47. package/dist/turn_detector/index.d.ts.map +1 -0
  48. package/dist/turn_detector/index.js +23 -0
  49. package/dist/turn_detector/index.js.map +1 -0
  50. package/dist/turn_detector/multilingual.cjs +144 -0
  51. package/dist/turn_detector/multilingual.cjs.map +1 -0
  52. package/dist/turn_detector/multilingual.d.cts +15 -0
  53. package/dist/turn_detector/multilingual.d.ts +15 -0
  54. package/dist/turn_detector/multilingual.d.ts.map +1 -0
  55. package/dist/turn_detector/multilingual.js +118 -0
  56. package/dist/turn_detector/multilingual.js.map +1 -0
  57. package/dist/turn_detector/utils.cjs +54 -0
  58. package/dist/turn_detector/utils.cjs.map +1 -0
  59. package/dist/turn_detector/utils.d.cts +35 -0
  60. package/dist/turn_detector/utils.d.ts +35 -0
  61. package/dist/turn_detector/utils.d.ts.map +1 -0
  62. package/dist/turn_detector/utils.js +29 -0
  63. package/dist/turn_detector/utils.js.map +1 -0
  64. package/dist/turn_detector/utils.test.cjs +196 -0
  65. package/dist/turn_detector/utils.test.cjs.map +1 -0
  66. package/dist/turn_detector/utils.test.d.cts +2 -0
  67. package/dist/turn_detector/utils.test.d.ts +2 -0
  68. package/dist/turn_detector/utils.test.d.ts.map +1 -0
  69. package/dist/turn_detector/utils.test.js +195 -0
  70. package/dist/turn_detector/utils.test.js.map +1 -0
  71. package/package.json +5 -4
  72. package/src/hf_utils.test.ts +392 -0
  73. package/src/hf_utils.ts +365 -0
  74. package/src/index.ts +32 -9
  75. package/src/turn_detector/base.ts +238 -0
  76. package/src/turn_detector/constants.ts +16 -0
  77. package/src/turn_detector/english.ts +27 -0
  78. package/src/turn_detector/index.ts +21 -0
  79. package/src/turn_detector/multilingual.ts +145 -0
  80. package/src/turn_detector/utils.test.ts +231 -0
  81. package/src/turn_detector/utils.ts +76 -0
  82. package/dist/turn_detector.cjs +0 -129
  83. package/dist/turn_detector.cjs.map +0 -1
  84. package/dist/turn_detector.d.cts +0 -22
  85. package/dist/turn_detector.d.ts +0 -22
  86. package/dist/turn_detector.d.ts.map +0 -1
  87. package/dist/turn_detector.js +0 -102
  88. package/dist/turn_detector.js.map +0 -1
  89. package/dist/turn_detector.onnx +0 -0
  90. package/src/turn_detector.onnx +0 -0
  91. package/src/turn_detector.ts +0 -121
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var base_exports = {};
30
+ __export(base_exports, {
31
+ EOUModel: () => EOUModel,
32
+ EOURunnerBase: () => EOURunnerBase
33
+ });
34
+ module.exports = __toCommonJS(base_exports);
35
+ var import_transformers = require("@huggingface/transformers");
36
+ var import_agents = require("@livekit/agents");
37
+ var import_node_fs = require("node:fs");
38
+ var import_node_os = __toESM(require("node:os"), 1);
39
+ var import_onnxruntime_node = require("onnxruntime-node");
40
+ var import_hf_utils = require("../hf_utils.cjs");
41
+ var import_constants = require("./constants.cjs");
42
+ var import_utils = require("./utils.cjs");
43
+ class EOURunnerBase extends import_agents.InferenceRunner {
44
+ modelType;
45
+ modelRevision;
46
+ session;
47
+ tokenizer;
48
+ #logger = (0, import_agents.log)();
49
+ constructor(modelType) {
50
+ super();
51
+ this.modelType = modelType;
52
+ this.modelRevision = import_constants.MODEL_REVISIONS[modelType];
53
+ }
54
+ async initialize() {
55
+ const { AutoTokenizer } = await import("@huggingface/transformers");
56
+ const onnxModelPath = await (0, import_hf_utils.downloadFileToCacheDir)({
57
+ repo: import_constants.HG_MODEL_REPO,
58
+ path: import_constants.ONNX_FILEPATH,
59
+ revision: this.modelRevision,
60
+ localFileOnly: true
61
+ });
62
+ try {
63
+ const sessOptions = {
64
+ intraOpNumThreads: Math.max(1, Math.floor(import_node_os.default.cpus().length / 2)),
65
+ interOpNumThreads: 1,
66
+ executionProviders: [{ name: "cpu" }]
67
+ };
68
+ this.session = await import_onnxruntime_node.InferenceSession.create(onnxModelPath, sessOptions);
69
+ this.tokenizer = await AutoTokenizer.from_pretrained("livekit/turn-detector", {
70
+ revision: this.modelRevision,
71
+ local_files_only: true
72
+ });
73
+ } catch (e) {
74
+ throw new Error(
75
+ `agents-plugins-livekit failed to initialize ${this.modelType} EOU turn detector: ${e}`
76
+ );
77
+ }
78
+ }
79
+ async run(data) {
80
+ const startTime = Date.now();
81
+ const text = this.formatChatCtx(data);
82
+ const inputs = this.tokenizer.encode(text, { add_special_tokens: false });
83
+ this.#logger.debug({ inputs: JSON.stringify(inputs), text }, "EOU inputs");
84
+ const outputs = await this.session.run(
85
+ { input_ids: new import_onnxruntime_node.Tensor("int64", inputs, [1, inputs.length]) },
86
+ ["prob"]
87
+ );
88
+ const probData = outputs.prob.data;
89
+ const eouProbability = probData[probData.length - 1];
90
+ const endTime = Date.now();
91
+ const result = {
92
+ eouProbability,
93
+ input: text,
94
+ duration: (endTime - startTime) / 1e3
95
+ };
96
+ this.#logger.child({ result }).debug("eou prediction");
97
+ return result;
98
+ }
99
+ async close() {
100
+ var _a;
101
+ await ((_a = this.session) == null ? void 0 : _a.release());
102
+ }
103
+ formatChatCtx(chatCtx) {
104
+ const newChatCtx = [];
105
+ let lastMsg = void 0;
106
+ for (const msg of chatCtx) {
107
+ const content = msg.content;
108
+ if (!content) continue;
109
+ const norm = (0, import_utils.normalizeText)(content);
110
+ if (lastMsg !== void 0 && lastMsg.role === msg.role) {
111
+ lastMsg.content += ` ${norm}`;
112
+ } else {
113
+ newChatCtx.push({ role: msg.role, content: norm });
114
+ lastMsg = newChatCtx[newChatCtx.length - 1];
115
+ }
116
+ }
117
+ const convoText = this.tokenizer.apply_chat_template(newChatCtx, {
118
+ add_generation_prompt: false,
119
+ tokenize: false
120
+ });
121
+ return convoText.slice(0, convoText.lastIndexOf("<|im_end|>"));
122
+ }
123
+ }
124
+ class EOUModel {
125
+ modelType;
126
+ executor;
127
+ threshold;
128
+ loadLanguages;
129
+ languagesFuture = new import_agents.Future();
130
+ #logger = (0, import_agents.log)();
131
+ constructor(opts) {
132
+ const {
133
+ modelType = "en",
134
+ executor = import_agents.CurrentJobContext.getCurrent().inferenceExecutor,
135
+ unlikelyThreshold,
136
+ loadLanguages = true
137
+ } = opts;
138
+ this.modelType = modelType;
139
+ this.executor = executor;
140
+ this.threshold = unlikelyThreshold;
141
+ this.loadLanguages = loadLanguages;
142
+ if (loadLanguages) {
143
+ (0, import_hf_utils.downloadFileToCacheDir)({
144
+ repo: import_constants.HG_MODEL_REPO,
145
+ path: "languages.json",
146
+ revision: import_constants.MODEL_REVISIONS[modelType],
147
+ localFileOnly: true
148
+ }).then((path) => {
149
+ this.languagesFuture.resolve(JSON.parse((0, import_node_fs.readFileSync)(path, "utf8")));
150
+ });
151
+ }
152
+ }
153
+ async unlikelyThreshold(language) {
154
+ if (language === void 0) {
155
+ return this.threshold;
156
+ }
157
+ const lang = language.toLowerCase();
158
+ const languages = await this.languagesFuture.await;
159
+ let langData = languages[lang];
160
+ if (langData === void 0 && lang.includes("-")) {
161
+ const baseLang = lang.split("-")[0];
162
+ langData = languages[baseLang];
163
+ }
164
+ if (langData === void 0) {
165
+ this.#logger.warn(`Language ${language} not supported by EOU model`);
166
+ return void 0;
167
+ }
168
+ return this.threshold !== void 0 ? this.threshold : langData.threshold;
169
+ }
170
+ async supportsLanguage(language) {
171
+ return await this.unlikelyThreshold(language) !== void 0;
172
+ }
173
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
174
+ async predictEndOfTurn(chatCtx, timeout = 3) {
175
+ let messages = [];
176
+ for (const message of chatCtx.items) {
177
+ if (message.type !== "message" || message.role in ["system", "developer"]) {
178
+ continue;
179
+ }
180
+ for (const content of message.content) {
181
+ if (typeof content === "string") {
182
+ messages.push({
183
+ role: message.role === "assistant" ? "assistant" : "user",
184
+ content
185
+ });
186
+ }
187
+ }
188
+ }
189
+ messages = messages.slice(-import_constants.MAX_HISTORY_TURNS);
190
+ const result = await this.executor.doInference(this.inferenceMethod(), messages);
191
+ if (result === void 0) {
192
+ throw new Error("EOU inference should always returns a result");
193
+ }
194
+ return result.eouProbability;
195
+ }
196
+ }
197
+ // Annotate the CommonJS export names for ESM import in node:
198
+ 0 && (module.exports = {
199
+ EOUModel,
200
+ EOURunnerBase
201
+ });
202
+ //# sourceMappingURL=base.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/turn_detector/base.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { type PreTrainedTokenizer } from '@huggingface/transformers';\nimport type { ipc, llm } from '@livekit/agents';\nimport { CurrentJobContext, Future, InferenceRunner, log } from '@livekit/agents';\nimport { readFileSync } from 'node:fs';\nimport os from 'node:os';\nimport { InferenceSession, Tensor } from 'onnxruntime-node';\nimport { downloadFileToCacheDir } from '../hf_utils.js';\nimport {\n type EOUModelType,\n HG_MODEL_REPO,\n MAX_HISTORY_TURNS,\n MODEL_REVISIONS,\n ONNX_FILEPATH,\n} from './constants.js';\nimport { normalizeText } from './utils.js';\n\ntype RawChatItem = { role: string; content: string };\n\ntype EOUOutput = { eouProbability: number; input: string; duration: number };\n\nexport abstract class EOURunnerBase extends InferenceRunner<RawChatItem[], EOUOutput> {\n private modelType: EOUModelType;\n private modelRevision: string;\n\n private session?: InferenceSession;\n private tokenizer?: PreTrainedTokenizer;\n\n #logger = log();\n\n constructor(modelType: EOUModelType) {\n super();\n this.modelType = modelType;\n this.modelRevision = MODEL_REVISIONS[modelType];\n }\n\n async initialize() {\n const { AutoTokenizer } = await import('@huggingface/transformers');\n\n const onnxModelPath = await downloadFileToCacheDir({\n repo: HG_MODEL_REPO,\n path: ONNX_FILEPATH,\n revision: this.modelRevision,\n localFileOnly: true,\n });\n\n try {\n // TODO(brian): support session config once onnxruntime-node supports it\n const sessOptions: InferenceSession.SessionOptions = {\n intraOpNumThreads: Math.max(1, Math.floor(os.cpus().length / 2)),\n interOpNumThreads: 1,\n executionProviders: [{ name: 'cpu' }],\n };\n\n this.session = await InferenceSession.create(onnxModelPath, sessOptions);\n\n this.tokenizer = await AutoTokenizer.from_pretrained('livekit/turn-detector', {\n revision: this.modelRevision,\n local_files_only: true,\n });\n } catch (e) {\n throw new Error(\n `agents-plugins-livekit failed to initialize ${this.modelType} EOU turn detector: ${e}`,\n );\n }\n }\n\n async run(data: RawChatItem[]) {\n const startTime = Date.now();\n\n const text = this.formatChatCtx(data);\n\n const inputs = this.tokenizer!.encode(text, { add_special_tokens: false });\n this.#logger.debug({ inputs: JSON.stringify(inputs), text }, 'EOU inputs');\n\n const outputs = await this.session!.run(\n { input_ids: new Tensor('int64', inputs, [1, inputs.length]) },\n ['prob'],\n );\n\n const probData = outputs.prob!.data;\n // should be the logits of the last token\n const eouProbability = probData[probData.length - 1] as number;\n const endTime = Date.now();\n\n const result = {\n eouProbability,\n input: text,\n duration: (endTime - startTime) / 1000,\n };\n\n this.#logger.child({ result }).debug('eou prediction');\n return result;\n }\n\n async close() {\n await this.session?.release();\n }\n\n private formatChatCtx(chatCtx: RawChatItem[]): string {\n const newChatCtx: RawChatItem[] = [];\n let lastMsg: RawChatItem | undefined = undefined;\n\n for (const msg of chatCtx) {\n const content = msg.content;\n if (!content) continue;\n\n const norm = normalizeText(content);\n\n // need to combine adjacent turns together to match training data\n if (lastMsg !== undefined && lastMsg.role === msg.role) {\n lastMsg.content += ` ${norm}`;\n } else {\n newChatCtx.push({ role: msg.role, content: norm });\n lastMsg = newChatCtx[newChatCtx.length - 1]!;\n }\n }\n\n // TODO(brian): investigate add_special_tokens options\n const convoText = this.tokenizer!.apply_chat_template(newChatCtx, {\n add_generation_prompt: false,\n tokenize: false,\n }) as string;\n\n // remove the EOU token from current utterance\n return convoText.slice(0, convoText.lastIndexOf('<|im_end|>'));\n }\n}\n\nexport interface EOUModelOptions {\n modelType: EOUModelType;\n executor?: ipc.InferenceExecutor;\n unlikelyThreshold?: number;\n loadLanguages?: boolean;\n}\n\ntype LanguageData = {\n threshold: number;\n};\n\nexport abstract class EOUModel {\n private modelType: EOUModelType;\n private executor: ipc.InferenceExecutor;\n private threshold: number | undefined;\n private loadLanguages: boolean;\n\n protected languagesFuture: Future<Record<string, LanguageData>> = new Future();\n\n #logger = log();\n\n constructor(opts: EOUModelOptions) {\n const {\n modelType = 'en',\n executor = CurrentJobContext.getCurrent().inferenceExecutor,\n unlikelyThreshold,\n loadLanguages = true,\n } = opts;\n\n this.modelType = modelType;\n this.executor = executor;\n this.threshold = unlikelyThreshold;\n this.loadLanguages = loadLanguages;\n\n if (loadLanguages) {\n downloadFileToCacheDir({\n repo: HG_MODEL_REPO,\n path: 'languages.json',\n revision: MODEL_REVISIONS[modelType],\n localFileOnly: true,\n }).then((path) => {\n this.languagesFuture.resolve(JSON.parse(readFileSync(path, 'utf8')));\n });\n }\n }\n\n async unlikelyThreshold(language?: string): Promise<number | undefined> {\n if (language === undefined) {\n return this.threshold;\n }\n\n const lang = language.toLowerCase();\n const languages = await this.languagesFuture.await;\n\n // try the full language code first\n let langData = languages[lang];\n\n if (langData === undefined && lang.includes('-')) {\n const baseLang = lang.split('-')[0]!;\n langData = languages[baseLang];\n }\n\n if (langData === undefined) {\n this.#logger.warn(`Language ${language} not supported by EOU model`);\n return undefined;\n }\n\n // if a custom threshold is provided, use it\n return this.threshold !== undefined ? this.threshold : langData.threshold;\n }\n\n async supportsLanguage(language?: string): Promise<boolean> {\n return (await this.unlikelyThreshold(language)) !== undefined;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async predictEndOfTurn(chatCtx: llm.ChatContext, timeout: number = 3): Promise<number> {\n let messages: RawChatItem[] = [];\n\n for (const message of chatCtx.items) {\n // skip system and developer messages or tool call messages\n if (message.type !== 'message' || message.role in ['system', 'developer']) {\n continue;\n }\n\n for (const content of message.content) {\n if (typeof content === 'string') {\n messages.push({\n role: message.role === 'assistant' ? 'assistant' : 'user',\n content: content,\n });\n }\n }\n }\n\n messages = messages.slice(-MAX_HISTORY_TURNS);\n\n const result = await this.executor.doInference(this.inferenceMethod(), messages);\n if (result === undefined) {\n throw new Error('EOU inference should always returns a result');\n }\n\n return (result as EOUOutput).eouProbability;\n }\n\n abstract inferenceMethod(): string;\n}\n"],"mappings":"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","names":["os"]}
@@ -0,0 +1,52 @@
1
+ import type { ipc, llm } from '@livekit/agents';
2
+ import { Future, InferenceRunner } from '@livekit/agents';
3
+ import { type EOUModelType } from './constants.js';
4
+ type RawChatItem = {
5
+ role: string;
6
+ content: string;
7
+ };
8
+ type EOUOutput = {
9
+ eouProbability: number;
10
+ input: string;
11
+ duration: number;
12
+ };
13
+ export declare abstract class EOURunnerBase extends InferenceRunner<RawChatItem[], EOUOutput> {
14
+ #private;
15
+ private modelType;
16
+ private modelRevision;
17
+ private session?;
18
+ private tokenizer?;
19
+ constructor(modelType: EOUModelType);
20
+ initialize(): Promise<void>;
21
+ run(data: RawChatItem[]): Promise<{
22
+ eouProbability: number;
23
+ input: string;
24
+ duration: number;
25
+ }>;
26
+ close(): Promise<void>;
27
+ private formatChatCtx;
28
+ }
29
+ export interface EOUModelOptions {
30
+ modelType: EOUModelType;
31
+ executor?: ipc.InferenceExecutor;
32
+ unlikelyThreshold?: number;
33
+ loadLanguages?: boolean;
34
+ }
35
+ type LanguageData = {
36
+ threshold: number;
37
+ };
38
+ export declare abstract class EOUModel {
39
+ #private;
40
+ private modelType;
41
+ private executor;
42
+ private threshold;
43
+ private loadLanguages;
44
+ protected languagesFuture: Future<Record<string, LanguageData>>;
45
+ constructor(opts: EOUModelOptions);
46
+ unlikelyThreshold(language?: string): Promise<number | undefined>;
47
+ supportsLanguage(language?: string): Promise<boolean>;
48
+ predictEndOfTurn(chatCtx: llm.ChatContext, timeout?: number): Promise<number>;
49
+ abstract inferenceMethod(): string;
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1,52 @@
1
+ import type { ipc, llm } from '@livekit/agents';
2
+ import { Future, InferenceRunner } from '@livekit/agents';
3
+ import { type EOUModelType } from './constants.js';
4
+ type RawChatItem = {
5
+ role: string;
6
+ content: string;
7
+ };
8
+ type EOUOutput = {
9
+ eouProbability: number;
10
+ input: string;
11
+ duration: number;
12
+ };
13
+ export declare abstract class EOURunnerBase extends InferenceRunner<RawChatItem[], EOUOutput> {
14
+ #private;
15
+ private modelType;
16
+ private modelRevision;
17
+ private session?;
18
+ private tokenizer?;
19
+ constructor(modelType: EOUModelType);
20
+ initialize(): Promise<void>;
21
+ run(data: RawChatItem[]): Promise<{
22
+ eouProbability: number;
23
+ input: string;
24
+ duration: number;
25
+ }>;
26
+ close(): Promise<void>;
27
+ private formatChatCtx;
28
+ }
29
+ export interface EOUModelOptions {
30
+ modelType: EOUModelType;
31
+ executor?: ipc.InferenceExecutor;
32
+ unlikelyThreshold?: number;
33
+ loadLanguages?: boolean;
34
+ }
35
+ type LanguageData = {
36
+ threshold: number;
37
+ };
38
+ export declare abstract class EOUModel {
39
+ #private;
40
+ private modelType;
41
+ private executor;
42
+ private threshold;
43
+ private loadLanguages;
44
+ protected languagesFuture: Future<Record<string, LanguageData>>;
45
+ constructor(opts: EOUModelOptions);
46
+ unlikelyThreshold(language?: string): Promise<number | undefined>;
47
+ supportsLanguage(language?: string): Promise<boolean>;
48
+ predictEndOfTurn(chatCtx: llm.ChatContext, timeout?: number): Promise<number>;
49
+ abstract inferenceMethod(): string;
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/turn_detector/base.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAqB,MAAM,EAAE,eAAe,EAAO,MAAM,iBAAiB,CAAC;AAKlF,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,gBAAgB,CAAC;AAGxB,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,KAAK,SAAS,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,8BAAsB,aAAc,SAAQ,eAAe,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC;;IACnF,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,OAAO,CAAC,CAAmB;IACnC,OAAO,CAAC,SAAS,CAAC,CAAsB;gBAI5B,SAAS,EAAE,YAAY;IAM7B,UAAU;IA+BV,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE;;;;;IA4BvB,KAAK;IAIX,OAAO,CAAC,aAAa;CA4BtB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,KAAK,YAAY,GAAG;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,8BAAsB,QAAQ;;IAC5B,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,aAAa,CAAU;IAE/B,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAgB;gBAInE,IAAI,EAAE,eAAe;IAyB3B,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAyBjE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,gBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE,OAAO,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IA6BtF,QAAQ,CAAC,eAAe,IAAI,MAAM;CACnC"}
@@ -0,0 +1,172 @@
1
+ import {} from "@huggingface/transformers";
2
+ import { CurrentJobContext, Future, InferenceRunner, log } from "@livekit/agents";
3
+ import { readFileSync } from "node:fs";
4
+ import os from "node:os";
5
+ import { InferenceSession, Tensor } from "onnxruntime-node";
6
+ import { downloadFileToCacheDir } from "../hf_utils.js";
7
+ import {
8
+ HG_MODEL_REPO,
9
+ MAX_HISTORY_TURNS,
10
+ MODEL_REVISIONS,
11
+ ONNX_FILEPATH
12
+ } from "./constants.js";
13
+ import { normalizeText } from "./utils.js";
14
+ class EOURunnerBase extends InferenceRunner {
15
+ modelType;
16
+ modelRevision;
17
+ session;
18
+ tokenizer;
19
+ #logger = log();
20
+ constructor(modelType) {
21
+ super();
22
+ this.modelType = modelType;
23
+ this.modelRevision = MODEL_REVISIONS[modelType];
24
+ }
25
+ async initialize() {
26
+ const { AutoTokenizer } = await import("@huggingface/transformers");
27
+ const onnxModelPath = await downloadFileToCacheDir({
28
+ repo: HG_MODEL_REPO,
29
+ path: ONNX_FILEPATH,
30
+ revision: this.modelRevision,
31
+ localFileOnly: true
32
+ });
33
+ try {
34
+ const sessOptions = {
35
+ intraOpNumThreads: Math.max(1, Math.floor(os.cpus().length / 2)),
36
+ interOpNumThreads: 1,
37
+ executionProviders: [{ name: "cpu" }]
38
+ };
39
+ this.session = await InferenceSession.create(onnxModelPath, sessOptions);
40
+ this.tokenizer = await AutoTokenizer.from_pretrained("livekit/turn-detector", {
41
+ revision: this.modelRevision,
42
+ local_files_only: true
43
+ });
44
+ } catch (e) {
45
+ throw new Error(
46
+ `agents-plugins-livekit failed to initialize ${this.modelType} EOU turn detector: ${e}`
47
+ );
48
+ }
49
+ }
50
+ async run(data) {
51
+ const startTime = Date.now();
52
+ const text = this.formatChatCtx(data);
53
+ const inputs = this.tokenizer.encode(text, { add_special_tokens: false });
54
+ this.#logger.debug({ inputs: JSON.stringify(inputs), text }, "EOU inputs");
55
+ const outputs = await this.session.run(
56
+ { input_ids: new Tensor("int64", inputs, [1, inputs.length]) },
57
+ ["prob"]
58
+ );
59
+ const probData = outputs.prob.data;
60
+ const eouProbability = probData[probData.length - 1];
61
+ const endTime = Date.now();
62
+ const result = {
63
+ eouProbability,
64
+ input: text,
65
+ duration: (endTime - startTime) / 1e3
66
+ };
67
+ this.#logger.child({ result }).debug("eou prediction");
68
+ return result;
69
+ }
70
+ async close() {
71
+ var _a;
72
+ await ((_a = this.session) == null ? void 0 : _a.release());
73
+ }
74
+ formatChatCtx(chatCtx) {
75
+ const newChatCtx = [];
76
+ let lastMsg = void 0;
77
+ for (const msg of chatCtx) {
78
+ const content = msg.content;
79
+ if (!content) continue;
80
+ const norm = normalizeText(content);
81
+ if (lastMsg !== void 0 && lastMsg.role === msg.role) {
82
+ lastMsg.content += ` ${norm}`;
83
+ } else {
84
+ newChatCtx.push({ role: msg.role, content: norm });
85
+ lastMsg = newChatCtx[newChatCtx.length - 1];
86
+ }
87
+ }
88
+ const convoText = this.tokenizer.apply_chat_template(newChatCtx, {
89
+ add_generation_prompt: false,
90
+ tokenize: false
91
+ });
92
+ return convoText.slice(0, convoText.lastIndexOf("<|im_end|>"));
93
+ }
94
+ }
95
+ class EOUModel {
96
+ modelType;
97
+ executor;
98
+ threshold;
99
+ loadLanguages;
100
+ languagesFuture = new Future();
101
+ #logger = log();
102
+ constructor(opts) {
103
+ const {
104
+ modelType = "en",
105
+ executor = CurrentJobContext.getCurrent().inferenceExecutor,
106
+ unlikelyThreshold,
107
+ loadLanguages = true
108
+ } = opts;
109
+ this.modelType = modelType;
110
+ this.executor = executor;
111
+ this.threshold = unlikelyThreshold;
112
+ this.loadLanguages = loadLanguages;
113
+ if (loadLanguages) {
114
+ downloadFileToCacheDir({
115
+ repo: HG_MODEL_REPO,
116
+ path: "languages.json",
117
+ revision: MODEL_REVISIONS[modelType],
118
+ localFileOnly: true
119
+ }).then((path) => {
120
+ this.languagesFuture.resolve(JSON.parse(readFileSync(path, "utf8")));
121
+ });
122
+ }
123
+ }
124
+ async unlikelyThreshold(language) {
125
+ if (language === void 0) {
126
+ return this.threshold;
127
+ }
128
+ const lang = language.toLowerCase();
129
+ const languages = await this.languagesFuture.await;
130
+ let langData = languages[lang];
131
+ if (langData === void 0 && lang.includes("-")) {
132
+ const baseLang = lang.split("-")[0];
133
+ langData = languages[baseLang];
134
+ }
135
+ if (langData === void 0) {
136
+ this.#logger.warn(`Language ${language} not supported by EOU model`);
137
+ return void 0;
138
+ }
139
+ return this.threshold !== void 0 ? this.threshold : langData.threshold;
140
+ }
141
+ async supportsLanguage(language) {
142
+ return await this.unlikelyThreshold(language) !== void 0;
143
+ }
144
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
145
+ async predictEndOfTurn(chatCtx, timeout = 3) {
146
+ let messages = [];
147
+ for (const message of chatCtx.items) {
148
+ if (message.type !== "message" || message.role in ["system", "developer"]) {
149
+ continue;
150
+ }
151
+ for (const content of message.content) {
152
+ if (typeof content === "string") {
153
+ messages.push({
154
+ role: message.role === "assistant" ? "assistant" : "user",
155
+ content
156
+ });
157
+ }
158
+ }
159
+ }
160
+ messages = messages.slice(-MAX_HISTORY_TURNS);
161
+ const result = await this.executor.doInference(this.inferenceMethod(), messages);
162
+ if (result === void 0) {
163
+ throw new Error("EOU inference should always returns a result");
164
+ }
165
+ return result.eouProbability;
166
+ }
167
+ }
168
+ export {
169
+ EOUModel,
170
+ EOURunnerBase
171
+ };
172
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/turn_detector/base.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { type PreTrainedTokenizer } from '@huggingface/transformers';\nimport type { ipc, llm } from '@livekit/agents';\nimport { CurrentJobContext, Future, InferenceRunner, log } from '@livekit/agents';\nimport { readFileSync } from 'node:fs';\nimport os from 'node:os';\nimport { InferenceSession, Tensor } from 'onnxruntime-node';\nimport { downloadFileToCacheDir } from '../hf_utils.js';\nimport {\n type EOUModelType,\n HG_MODEL_REPO,\n MAX_HISTORY_TURNS,\n MODEL_REVISIONS,\n ONNX_FILEPATH,\n} from './constants.js';\nimport { normalizeText } from './utils.js';\n\ntype RawChatItem = { role: string; content: string };\n\ntype EOUOutput = { eouProbability: number; input: string; duration: number };\n\nexport abstract class EOURunnerBase extends InferenceRunner<RawChatItem[], EOUOutput> {\n private modelType: EOUModelType;\n private modelRevision: string;\n\n private session?: InferenceSession;\n private tokenizer?: PreTrainedTokenizer;\n\n #logger = log();\n\n constructor(modelType: EOUModelType) {\n super();\n this.modelType = modelType;\n this.modelRevision = MODEL_REVISIONS[modelType];\n }\n\n async initialize() {\n const { AutoTokenizer } = await import('@huggingface/transformers');\n\n const onnxModelPath = await downloadFileToCacheDir({\n repo: HG_MODEL_REPO,\n path: ONNX_FILEPATH,\n revision: this.modelRevision,\n localFileOnly: true,\n });\n\n try {\n // TODO(brian): support session config once onnxruntime-node supports it\n const sessOptions: InferenceSession.SessionOptions = {\n intraOpNumThreads: Math.max(1, Math.floor(os.cpus().length / 2)),\n interOpNumThreads: 1,\n executionProviders: [{ name: 'cpu' }],\n };\n\n this.session = await InferenceSession.create(onnxModelPath, sessOptions);\n\n this.tokenizer = await AutoTokenizer.from_pretrained('livekit/turn-detector', {\n revision: this.modelRevision,\n local_files_only: true,\n });\n } catch (e) {\n throw new Error(\n `agents-plugins-livekit failed to initialize ${this.modelType} EOU turn detector: ${e}`,\n );\n }\n }\n\n async run(data: RawChatItem[]) {\n const startTime = Date.now();\n\n const text = this.formatChatCtx(data);\n\n const inputs = this.tokenizer!.encode(text, { add_special_tokens: false });\n this.#logger.debug({ inputs: JSON.stringify(inputs), text }, 'EOU inputs');\n\n const outputs = await this.session!.run(\n { input_ids: new Tensor('int64', inputs, [1, inputs.length]) },\n ['prob'],\n );\n\n const probData = outputs.prob!.data;\n // should be the logits of the last token\n const eouProbability = probData[probData.length - 1] as number;\n const endTime = Date.now();\n\n const result = {\n eouProbability,\n input: text,\n duration: (endTime - startTime) / 1000,\n };\n\n this.#logger.child({ result }).debug('eou prediction');\n return result;\n }\n\n async close() {\n await this.session?.release();\n }\n\n private formatChatCtx(chatCtx: RawChatItem[]): string {\n const newChatCtx: RawChatItem[] = [];\n let lastMsg: RawChatItem | undefined = undefined;\n\n for (const msg of chatCtx) {\n const content = msg.content;\n if (!content) continue;\n\n const norm = normalizeText(content);\n\n // need to combine adjacent turns together to match training data\n if (lastMsg !== undefined && lastMsg.role === msg.role) {\n lastMsg.content += ` ${norm}`;\n } else {\n newChatCtx.push({ role: msg.role, content: norm });\n lastMsg = newChatCtx[newChatCtx.length - 1]!;\n }\n }\n\n // TODO(brian): investigate add_special_tokens options\n const convoText = this.tokenizer!.apply_chat_template(newChatCtx, {\n add_generation_prompt: false,\n tokenize: false,\n }) as string;\n\n // remove the EOU token from current utterance\n return convoText.slice(0, convoText.lastIndexOf('<|im_end|>'));\n }\n}\n\nexport interface EOUModelOptions {\n modelType: EOUModelType;\n executor?: ipc.InferenceExecutor;\n unlikelyThreshold?: number;\n loadLanguages?: boolean;\n}\n\ntype LanguageData = {\n threshold: number;\n};\n\nexport abstract class EOUModel {\n private modelType: EOUModelType;\n private executor: ipc.InferenceExecutor;\n private threshold: number | undefined;\n private loadLanguages: boolean;\n\n protected languagesFuture: Future<Record<string, LanguageData>> = new Future();\n\n #logger = log();\n\n constructor(opts: EOUModelOptions) {\n const {\n modelType = 'en',\n executor = CurrentJobContext.getCurrent().inferenceExecutor,\n unlikelyThreshold,\n loadLanguages = true,\n } = opts;\n\n this.modelType = modelType;\n this.executor = executor;\n this.threshold = unlikelyThreshold;\n this.loadLanguages = loadLanguages;\n\n if (loadLanguages) {\n downloadFileToCacheDir({\n repo: HG_MODEL_REPO,\n path: 'languages.json',\n revision: MODEL_REVISIONS[modelType],\n localFileOnly: true,\n }).then((path) => {\n this.languagesFuture.resolve(JSON.parse(readFileSync(path, 'utf8')));\n });\n }\n }\n\n async unlikelyThreshold(language?: string): Promise<number | undefined> {\n if (language === undefined) {\n return this.threshold;\n }\n\n const lang = language.toLowerCase();\n const languages = await this.languagesFuture.await;\n\n // try the full language code first\n let langData = languages[lang];\n\n if (langData === undefined && lang.includes('-')) {\n const baseLang = lang.split('-')[0]!;\n langData = languages[baseLang];\n }\n\n if (langData === undefined) {\n this.#logger.warn(`Language ${language} not supported by EOU model`);\n return undefined;\n }\n\n // if a custom threshold is provided, use it\n return this.threshold !== undefined ? this.threshold : langData.threshold;\n }\n\n async supportsLanguage(language?: string): Promise<boolean> {\n return (await this.unlikelyThreshold(language)) !== undefined;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async predictEndOfTurn(chatCtx: llm.ChatContext, timeout: number = 3): Promise<number> {\n let messages: RawChatItem[] = [];\n\n for (const message of chatCtx.items) {\n // skip system and developer messages or tool call messages\n if (message.type !== 'message' || message.role in ['system', 'developer']) {\n continue;\n }\n\n for (const content of message.content) {\n if (typeof content === 'string') {\n messages.push({\n role: message.role === 'assistant' ? 'assistant' : 'user',\n content: content,\n });\n }\n }\n }\n\n messages = messages.slice(-MAX_HISTORY_TURNS);\n\n const result = await this.executor.doInference(this.inferenceMethod(), messages);\n if (result === undefined) {\n throw new Error('EOU inference should always returns a result');\n }\n\n return (result as EOUOutput).eouProbability;\n }\n\n abstract inferenceMethod(): string;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var constants_exports = {};
20
+ __export(constants_exports, {
21
+ HG_MODEL_REPO: () => HG_MODEL_REPO,
22
+ MAX_HISTORY_TOKENS: () => MAX_HISTORY_TOKENS,
23
+ MAX_HISTORY_TURNS: () => MAX_HISTORY_TURNS,
24
+ MODEL_REVISIONS: () => MODEL_REVISIONS,
25
+ ONNX_FILEPATH: () => ONNX_FILEPATH
26
+ });
27
+ module.exports = __toCommonJS(constants_exports);
28
+ const MAX_HISTORY_TOKENS = 128;
29
+ const MAX_HISTORY_TURNS = 6;
30
+ const MODEL_REVISIONS = {
31
+ en: "v1.2.2-en",
32
+ multilingual: "v0.3.0-intl"
33
+ };
34
+ const HG_MODEL_REPO = "livekit/turn-detector";
35
+ const ONNX_FILEPATH = "onnx/model_q8.onnx";
36
+ // Annotate the CommonJS export names for ESM import in node:
37
+ 0 && (module.exports = {
38
+ HG_MODEL_REPO,
39
+ MAX_HISTORY_TOKENS,
40
+ MAX_HISTORY_TURNS,
41
+ MODEL_REVISIONS,
42
+ ONNX_FILEPATH
43
+ });
44
+ //# sourceMappingURL=constants.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/turn_detector/constants.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nexport type EOUModelType = 'en' | 'multilingual';\n\nexport const MAX_HISTORY_TOKENS = 128;\nexport const MAX_HISTORY_TURNS = 6;\n\nexport const MODEL_REVISIONS: Record<EOUModelType, string> = {\n en: 'v1.2.2-en',\n multilingual: 'v0.3.0-intl',\n};\n\nexport const HG_MODEL_REPO = 'livekit/turn-detector';\n\nexport const ONNX_FILEPATH = 'onnx/model_q8.onnx';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AAE1B,MAAM,kBAAgD;AAAA,EAC3D,IAAI;AAAA,EACJ,cAAc;AAChB;AAEO,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;","names":[]}
@@ -0,0 +1,7 @@
1
+ export type EOUModelType = 'en' | 'multilingual';
2
+ export declare const MAX_HISTORY_TOKENS = 128;
3
+ export declare const MAX_HISTORY_TURNS = 6;
4
+ export declare const MODEL_REVISIONS: Record<EOUModelType, string>;
5
+ export declare const HG_MODEL_REPO = "livekit/turn-detector";
6
+ export declare const ONNX_FILEPATH = "onnx/model_q8.onnx";
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1,7 @@
1
+ export type EOUModelType = 'en' | 'multilingual';
2
+ export declare const MAX_HISTORY_TOKENS = 128;
3
+ export declare const MAX_HISTORY_TURNS = 6;
4
+ export declare const MODEL_REVISIONS: Record<EOUModelType, string>;
5
+ export declare const HG_MODEL_REPO = "livekit/turn-detector";
6
+ export declare const ONNX_FILEPATH = "onnx/model_q8.onnx";
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/turn_detector/constants.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,cAAc,CAAC;AAEjD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAGxD,CAAC;AAEF,eAAO,MAAM,aAAa,0BAA0B,CAAC;AAErD,eAAO,MAAM,aAAa,uBAAuB,CAAC"}
@@ -0,0 +1,16 @@
1
+ const MAX_HISTORY_TOKENS = 128;
2
+ const MAX_HISTORY_TURNS = 6;
3
+ const MODEL_REVISIONS = {
4
+ en: "v1.2.2-en",
5
+ multilingual: "v0.3.0-intl"
6
+ };
7
+ const HG_MODEL_REPO = "livekit/turn-detector";
8
+ const ONNX_FILEPATH = "onnx/model_q8.onnx";
9
+ export {
10
+ HG_MODEL_REPO,
11
+ MAX_HISTORY_TOKENS,
12
+ MAX_HISTORY_TURNS,
13
+ MODEL_REVISIONS,
14
+ ONNX_FILEPATH
15
+ };
16
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/turn_detector/constants.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nexport type EOUModelType = 'en' | 'multilingual';\n\nexport const MAX_HISTORY_TOKENS = 128;\nexport const MAX_HISTORY_TURNS = 6;\n\nexport const MODEL_REVISIONS: Record<EOUModelType, string> = {\n en: 'v1.2.2-en',\n multilingual: 'v0.3.0-intl',\n};\n\nexport const HG_MODEL_REPO = 'livekit/turn-detector';\n\nexport const ONNX_FILEPATH = 'onnx/model_q8.onnx';\n"],"mappings":"AAKO,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AAE1B,MAAM,kBAAgD;AAAA,EAC3D,IAAI;AAAA,EACJ,cAAc;AAChB;AAEO,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;","names":[]}