@livekit/agents-plugin-livekit 0.1.3 → 1.0.0-next.1

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,272 @@
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 hf_utils_exports = {};
30
+ __export(hf_utils_exports, {
31
+ REGEX_COMMIT_HASH: () => REGEX_COMMIT_HASH,
32
+ downloadFileToCacheDir: () => downloadFileToCacheDir
33
+ });
34
+ module.exports = __toCommonJS(hf_utils_exports);
35
+ var import_hub = require("@huggingface/hub");
36
+ var import_agents = require("@livekit/agents");
37
+ var import_node_fs = require("node:fs");
38
+ var import_promises = require("node:fs/promises");
39
+ var import_node_os = require("node:os");
40
+ var import_node_path = require("node:path");
41
+ var import_node_stream = require("node:stream");
42
+ var import_promises2 = require("node:stream/promises");
43
+ const REGEX_COMMIT_HASH = new RegExp("^[0-9a-f]{40}$");
44
+ function getHFHubCachePath(customCacheDir) {
45
+ return customCacheDir || (0, import_node_path.join)((0, import_node_os.homedir)(), ".cache", "huggingface", "hub");
46
+ }
47
+ function getRepoFolderName(repoId) {
48
+ return `models--${repoId.replace(/\//g, "--")}`;
49
+ }
50
+ function toRepoId(repo) {
51
+ if (typeof repo === "string") {
52
+ return repo;
53
+ }
54
+ return `${repo.name}`;
55
+ }
56
+ async function getBranchHeadCommit(repo, revision, params) {
57
+ const logger = (0, import_agents.log)();
58
+ try {
59
+ if (REGEX_COMMIT_HASH.test(revision)) {
60
+ return revision;
61
+ }
62
+ for await (const commit of (0, import_hub.listCommits)({
63
+ repo,
64
+ revision,
65
+ ...params
66
+ })) {
67
+ const commitHash = commit.oid || commit.id || commit.commitId;
68
+ if (commitHash) {
69
+ return commitHash;
70
+ }
71
+ break;
72
+ }
73
+ logger.error({ repo: toRepoId(repo), revision }, "No commits found for revision");
74
+ return null;
75
+ } catch (error) {
76
+ logger.error(
77
+ { error: error.message, repo: toRepoId(repo), revision },
78
+ "Error getting HEAD commit"
79
+ );
80
+ throw error;
81
+ }
82
+ }
83
+ async function createSymlink(sourcePath, targetPath) {
84
+ const logger = (0, import_agents.log)();
85
+ const { symlink, rm, copyFile } = await import("node:fs/promises");
86
+ function expandUser(path) {
87
+ if (path.startsWith("~")) {
88
+ return path.replace("~", (0, import_node_os.homedir)());
89
+ }
90
+ return path;
91
+ }
92
+ const absSrc = (0, import_node_path.resolve)(expandUser(sourcePath));
93
+ const absDst = (0, import_node_path.resolve)(expandUser(targetPath));
94
+ try {
95
+ await rm(absDst);
96
+ } catch {
97
+ }
98
+ try {
99
+ const relativePath = (0, import_node_path.relative)((0, import_node_path.dirname)(absDst), absSrc);
100
+ await symlink(relativePath, absDst);
101
+ logger.debug({ source: absSrc, target: absDst, relative: relativePath }, "Created symlink");
102
+ } catch (symlinkError) {
103
+ logger.warn({ source: absSrc, target: absDst }, "Symlink not supported, falling back to copy");
104
+ try {
105
+ await copyFile(absSrc, absDst);
106
+ logger.debug({ source: absSrc, target: absDst }, "File copied successfully");
107
+ } catch (copyError) {
108
+ logger.error(
109
+ { error: copyError.message, source: absSrc, target: absDst },
110
+ "Failed to copy file"
111
+ );
112
+ throw symlinkError;
113
+ }
114
+ }
115
+ }
116
+ function getFilePointer(storageFolder, revision, relativeFilename) {
117
+ const snapshotPath = (0, import_node_path.join)(storageFolder, "snapshots");
118
+ return (0, import_node_path.join)(snapshotPath, revision, relativeFilename);
119
+ }
120
+ async function exists(path, followSymlinks) {
121
+ try {
122
+ if (followSymlinks) {
123
+ await (0, import_promises.stat)(path);
124
+ } else {
125
+ await (0, import_promises.lstat)(path);
126
+ }
127
+ return true;
128
+ } catch (err) {
129
+ return false;
130
+ }
131
+ }
132
+ async function saveRevisionMapping({
133
+ storageFolder,
134
+ revision,
135
+ commitHash
136
+ }) {
137
+ if (!REGEX_COMMIT_HASH.test(revision) && revision !== commitHash) {
138
+ const refsPath = (0, import_node_path.join)(storageFolder, "refs");
139
+ await (0, import_promises.mkdir)(refsPath, { recursive: true });
140
+ (0, import_node_fs.writeFileSync)((0, import_node_path.join)(refsPath, revision), commitHash);
141
+ }
142
+ }
143
+ async function downloadFileToCacheDir(params) {
144
+ const logger = (0, import_agents.log)();
145
+ const revision = params.revision ?? "main";
146
+ const cacheDir = params.cacheDir ?? getHFHubCachePath();
147
+ const repoId = toRepoId(params.repo);
148
+ const storageFolder = (0, import_node_path.join)(cacheDir, getRepoFolderName(repoId));
149
+ let branchHeadCommit;
150
+ if (REGEX_COMMIT_HASH.test(revision)) {
151
+ branchHeadCommit = revision;
152
+ const pointerPath2 = getFilePointer(storageFolder, revision, params.path);
153
+ if (await exists(pointerPath2, true)) {
154
+ logger.debug(
155
+ { pointerPath: pointerPath2, commitHash: branchHeadCommit },
156
+ "File found in cache (commit hash)"
157
+ );
158
+ return pointerPath2;
159
+ }
160
+ }
161
+ if (params.localFileOnly) {
162
+ logger.debug({ repoId, path: params.path, revision }, "Local file only mode - checking cache");
163
+ const directPath = getFilePointer(storageFolder, revision, params.path);
164
+ if (await exists(directPath, true)) {
165
+ logger.debug({ directPath }, "File found in cache (direct path)");
166
+ return directPath;
167
+ }
168
+ if (!REGEX_COMMIT_HASH.test(revision)) {
169
+ const refsPath = (0, import_node_path.join)(storageFolder, "refs", revision);
170
+ try {
171
+ const { readFileSync } = await import("fs");
172
+ const resolvedHash = readFileSync(refsPath, "utf-8").trim();
173
+ const resolvedPath = getFilePointer(storageFolder, resolvedHash, params.path);
174
+ if (await exists(resolvedPath, true)) {
175
+ logger.debug({ resolvedPath, resolvedHash }, "File found in cache (via refs)");
176
+ return resolvedPath;
177
+ }
178
+ } catch {
179
+ logger.debug({ revision }, "No ref mapping found for revision");
180
+ }
181
+ }
182
+ const error = `File not found in cache: ${repoId}/${params.path} (revision: ${revision}). Make sure to run the download-files command before running the agent worker.`;
183
+ logger.error({ repoId, path: params.path, revision }, error);
184
+ throw new Error(error);
185
+ }
186
+ if (!branchHeadCommit) {
187
+ const headCommit = await getBranchHeadCommit(params.repo, revision, params);
188
+ if (!headCommit) {
189
+ throw new Error(`Failed to resolve revision ${revision} to commit hash`);
190
+ }
191
+ branchHeadCommit = headCommit;
192
+ }
193
+ const pointerPath = getFilePointer(storageFolder, branchHeadCommit, params.path);
194
+ if (await exists(pointerPath, true)) {
195
+ logger.debug({ pointerPath, branchHeadCommit }, "File found in cache (branch HEAD)");
196
+ await saveRevisionMapping({
197
+ storageFolder,
198
+ revision,
199
+ commitHash: branchHeadCommit
200
+ });
201
+ return pointerPath;
202
+ }
203
+ logger.debug(
204
+ { repoId, path: params.path, revision: branchHeadCommit },
205
+ "Fetching path info from HF API"
206
+ );
207
+ const pathsInformation = await (0, import_hub.pathsInfo)({
208
+ ...params,
209
+ paths: [params.path],
210
+ revision: branchHeadCommit,
211
+ // Use HEAD commit for consistency
212
+ expand: true
213
+ });
214
+ if (!pathsInformation || pathsInformation.length !== 1) {
215
+ const error = `cannot get path info for ${params.path}`;
216
+ logger.error({ repoId, path: params.path, pathsInfoLength: pathsInformation == null ? void 0 : pathsInformation.length }, error);
217
+ throw new Error(error);
218
+ }
219
+ const pathInfo = pathsInformation[0];
220
+ if (!pathInfo) {
221
+ const error = `No path info returned for ${params.path}`;
222
+ logger.error({ repoId, path: params.path }, error);
223
+ throw new Error(error);
224
+ }
225
+ let etag;
226
+ if (pathInfo.lfs) {
227
+ etag = pathInfo.lfs.oid;
228
+ logger.debug({ etag, path: params.path }, "File is LFS pointer");
229
+ } else {
230
+ etag = pathInfo.oid;
231
+ logger.debug({ etag, path: params.path }, "File is regular git object");
232
+ }
233
+ const blobPath = (0, import_node_path.join)(storageFolder, "blobs", etag);
234
+ logger.debug({ branchHeadCommit, pointerPath, blobPath }, "Computed cache paths");
235
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(blobPath), { recursive: true });
236
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(pointerPath), { recursive: true });
237
+ if (await exists(blobPath)) {
238
+ logger.debug({ blobPath, etag }, "Blob already exists in cache, creating symlink only");
239
+ await createSymlink(blobPath, pointerPath);
240
+ return pointerPath;
241
+ }
242
+ const incomplete = `${blobPath}.incomplete`;
243
+ logger.debug({ path: params.path, incomplete }, "Starting file download");
244
+ const blob = await (0, import_hub.downloadFile)({
245
+ ...params,
246
+ revision: branchHeadCommit
247
+ });
248
+ if (!blob) {
249
+ const error = `invalid response for file ${params.path}`;
250
+ logger.error({ path: params.path }, error);
251
+ throw new Error(error);
252
+ }
253
+ logger.debug({ size: blob.size }, "Writing blob to disk");
254
+ await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(blob.stream()), (0, import_node_fs.createWriteStream)(incomplete));
255
+ await (0, import_promises.rename)(incomplete, blobPath);
256
+ logger.debug({ blobPath }, "Renamed incomplete file to final blob");
257
+ await createSymlink(blobPath, pointerPath);
258
+ logger.debug({ blobPath, pointerPath }, "Created symlink from snapshot to blob");
259
+ await saveRevisionMapping({
260
+ storageFolder,
261
+ revision,
262
+ commitHash: branchHeadCommit
263
+ });
264
+ logger.debug({ pointerPath, size: blob.size }, "File download completed successfully");
265
+ return pointerPath;
266
+ }
267
+ // Annotate the CommonJS export names for ESM import in node:
268
+ 0 && (module.exports = {
269
+ REGEX_COMMIT_HASH,
270
+ downloadFileToCacheDir
271
+ });
272
+ //# sourceMappingURL=hf_utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hf_utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Fixed version of HuggingFace's downloadFileToCacheDir that matches Python's behavior\n *\n * Key fix: Uses branch/tag HEAD commit for snapshot paths, not file's last commit\n * This ensures all files from the same revision end up in the same snapshot folder\n */\nimport type { CommitInfo, PathInfo, RepoDesignation } from '@huggingface/hub';\nimport { downloadFile, listCommits, pathsInfo } from '@huggingface/hub';\nimport { log } from '@livekit/agents';\nimport { createWriteStream, writeFileSync } from 'node:fs';\nimport { lstat, mkdir, rename, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { ReadableStream } from 'node:stream/web';\n\n// Define CredentialsParams if not exported\ninterface CredentialsParams {\n accessToken?: string;\n}\n\nexport const REGEX_COMMIT_HASH: RegExp = new RegExp('^[0-9a-f]{40}$');\n\n// Helper functions from HuggingFace's cache-management\nfunction getHFHubCachePath(customCacheDir?: string): string {\n return customCacheDir || join(homedir(), '.cache', 'huggingface', 'hub');\n}\n\nfunction getRepoFolderName(repoId: string): string {\n return `models--${repoId.replace(/\\//g, '--')}`;\n}\n\nfunction toRepoId(repo: RepoDesignation | string): string {\n if (typeof repo === 'string') {\n return repo;\n }\n return `${repo.name}`;\n}\n\n/**\n * Get the HEAD commit hash for a branch/tag (matching Python's behavior)\n */\nasync function getBranchHeadCommit(\n repo: RepoDesignation,\n revision: string,\n params: { accessToken?: string; hubUrl?: string; fetch?: typeof fetch },\n): Promise<string | null> {\n const logger = log();\n\n try {\n // If already a commit hash, return it\n if (REGEX_COMMIT_HASH.test(revision)) {\n return revision;\n }\n\n // Get the first commit from listCommits - this is the HEAD\n for await (const commit of listCommits({\n repo,\n revision,\n ...params,\n })) {\n // The commit object structure varies, so we check multiple possible properties\n const commitHash = (commit as any).oid || (commit as any).id || (commit as any).commitId;\n if (commitHash) {\n return commitHash;\n }\n break; // Only need the first one\n }\n\n logger.error({ repo: toRepoId(repo), revision }, 'No commits found for revision');\n return null;\n } catch (error) {\n logger.error(\n { error: (error as Error).message, repo: toRepoId(repo), revision },\n 'Error getting HEAD commit',\n );\n throw error;\n }\n}\n\n/**\n * Create a symbolic link following HuggingFace's implementation\n */\nasync function createSymlink(sourcePath: string, targetPath: string): Promise<void> {\n const logger = log();\n const { symlink, rm, copyFile } = await import('node:fs/promises');\n\n // Expand ~ to home directory\n function expandUser(path: string): string {\n if (path.startsWith('~')) {\n return path.replace('~', homedir());\n }\n return path;\n }\n\n const absSrc = resolve(expandUser(sourcePath));\n const absDst = resolve(expandUser(targetPath));\n\n // Remove existing file/symlink if it exists\n try {\n await rm(absDst);\n } catch {\n // Ignore - file might not exist\n }\n\n try {\n // Create relative symlink (better for portability)\n const relativePath = relative(dirname(absDst), absSrc);\n await symlink(relativePath, absDst);\n logger.debug({ source: absSrc, target: absDst, relative: relativePath }, 'Created symlink');\n } catch (symlinkError) {\n // Symlink failed (common on Windows without admin rights)\n // Fall back to copying the file\n logger.warn({ source: absSrc, target: absDst }, 'Symlink not supported, falling back to copy');\n try {\n await copyFile(absSrc, absDst);\n logger.debug({ source: absSrc, target: absDst }, 'File copied successfully');\n } catch (copyError) {\n logger.error(\n { error: (copyError as Error).message, source: absSrc, target: absDst },\n 'Failed to copy file',\n );\n // If copy also fails, throw the original symlink error\n throw symlinkError;\n }\n }\n}\n\nfunction getFilePointer(storageFolder: string, revision: string, relativeFilename: string): string {\n const snapshotPath = join(storageFolder, 'snapshots');\n return join(snapshotPath, revision, relativeFilename);\n}\n\n/**\n * handy method to check if a file exists, or the pointer of a symlinks exists\n */\nasync function exists(path: string, followSymlinks?: boolean): Promise<boolean> {\n try {\n if (followSymlinks) {\n await stat(path);\n } else {\n await lstat(path);\n }\n return true;\n } catch (err: unknown) {\n return false;\n }\n}\n\nasync function saveRevisionMapping({\n storageFolder,\n revision,\n commitHash,\n}: {\n storageFolder: string;\n revision: string;\n commitHash: string;\n}): Promise<void> {\n if (!REGEX_COMMIT_HASH.test(revision) && revision !== commitHash) {\n const refsPath = join(storageFolder, 'refs');\n await mkdir(refsPath, { recursive: true });\n writeFileSync(join(refsPath, revision), commitHash);\n }\n}\n\n/**\n * Download a given file if it's not already present in the local cache.\n * Matches Python's hf_hub_download behavior by using branch HEAD commits.\n */\nexport async function downloadFileToCacheDir(\n params: {\n repo: RepoDesignation;\n path: string;\n /**\n * If true, will download the raw git file.\n */\n raw?: boolean;\n /**\n * An optional Git revision id which can be a branch name, a tag, or a commit hash.\n * @default \"main\"\n */\n revision?: string;\n hubUrl?: string;\n cacheDir?: string;\n /**\n * Custom fetch function to use instead of the default one\n */\n fetch?: typeof fetch;\n /**\n * If true, only return cached files, don't download\n */\n localFileOnly?: boolean;\n } & Partial<CredentialsParams>,\n): Promise<string> {\n const logger = log();\n\n // get revision provided or default to main\n const revision = params.revision ?? 'main';\n const cacheDir = params.cacheDir ?? getHFHubCachePath();\n // get repo id\n const repoId = toRepoId(params.repo);\n // get storage folder\n const storageFolder = join(cacheDir, getRepoFolderName(repoId));\n\n let branchHeadCommit: string | undefined;\n\n // if user provides a commitHash as revision, use it directly\n if (REGEX_COMMIT_HASH.test(revision)) {\n branchHeadCommit = revision;\n const pointerPath = getFilePointer(storageFolder, revision, params.path);\n if (await exists(pointerPath, true)) {\n logger.debug(\n { pointerPath, commitHash: branchHeadCommit },\n 'File found in cache (commit hash)',\n );\n return pointerPath;\n }\n }\n\n // If localFileOnly, check cache without making API calls\n if (params.localFileOnly) {\n logger.debug({ repoId, path: params.path, revision }, 'Local file only mode - checking cache');\n\n // Check with revision as-is (in case it's a commit hash)\n const directPath = getFilePointer(storageFolder, revision, params.path);\n if (await exists(directPath, true)) {\n logger.debug({ directPath }, 'File found in cache (direct path)');\n return directPath;\n }\n\n // If revision is not a commit hash, try to resolve from refs\n if (!REGEX_COMMIT_HASH.test(revision)) {\n const refsPath = join(storageFolder, 'refs', revision);\n try {\n const { readFileSync } = await import('fs');\n const resolvedHash = readFileSync(refsPath, 'utf-8').trim();\n const resolvedPath = getFilePointer(storageFolder, resolvedHash, params.path);\n if (await exists(resolvedPath, true)) {\n logger.debug({ resolvedPath, resolvedHash }, 'File found in cache (via refs)');\n return resolvedPath;\n }\n } catch {\n logger.debug({ revision }, 'No ref mapping found for revision');\n }\n }\n\n const error = `File not found in cache: ${repoId}/${params.path} (revision: ${revision}). Make sure to run the download-files command before running the agent worker.`;\n logger.error({ repoId, path: params.path, revision }, error);\n throw new Error(error);\n }\n\n // Get the branch HEAD commit if not already a commit hash\n if (!branchHeadCommit) {\n const headCommit = await getBranchHeadCommit(params.repo, revision, params);\n if (!headCommit) {\n throw new Error(`Failed to resolve revision ${revision} to commit hash`);\n }\n branchHeadCommit = headCommit;\n }\n\n // Check if file exists with the branch HEAD commit\n const pointerPath = getFilePointer(storageFolder, branchHeadCommit, params.path);\n if (await exists(pointerPath, true)) {\n logger.debug({ pointerPath, branchHeadCommit }, 'File found in cache (branch HEAD)');\n\n await saveRevisionMapping({\n storageFolder,\n revision,\n commitHash: branchHeadCommit,\n });\n\n return pointerPath;\n }\n\n // Now get file metadata to download it\n logger.debug(\n { repoId, path: params.path, revision: branchHeadCommit },\n 'Fetching path info from HF API',\n );\n const pathsInformation: (PathInfo & { lastCommit: CommitInfo })[] = await pathsInfo({\n ...params,\n paths: [params.path],\n revision: branchHeadCommit, // Use HEAD commit for consistency\n expand: true,\n });\n\n if (!pathsInformation || pathsInformation.length !== 1) {\n const error = `cannot get path info for ${params.path}`;\n logger.error({ repoId, path: params.path, pathsInfoLength: pathsInformation?.length }, error);\n throw new Error(error);\n }\n\n const pathInfo = pathsInformation[0];\n if (!pathInfo) {\n const error = `No path info returned for ${params.path}`;\n logger.error({ repoId, path: params.path }, error);\n throw new Error(error);\n }\n\n let etag: string;\n if (pathInfo.lfs) {\n etag = pathInfo.lfs.oid; // get the LFS pointed file oid\n logger.debug({ etag, path: params.path }, 'File is LFS pointer');\n } else {\n etag = pathInfo.oid; // get the repo file if not a LFS pointer\n logger.debug({ etag, path: params.path }, 'File is regular git object');\n }\n\n const blobPath = join(storageFolder, 'blobs', etag);\n\n logger.debug({ branchHeadCommit, pointerPath, blobPath }, 'Computed cache paths');\n\n // mkdir blob and pointer path parent directory\n await mkdir(dirname(blobPath), { recursive: true });\n await mkdir(dirname(pointerPath), { recursive: true });\n\n // We might already have the blob but not the pointer\n // shortcut the download if needed\n if (await exists(blobPath)) {\n logger.debug({ blobPath, etag }, 'Blob already exists in cache, creating symlink only');\n // create symlinks in snapshot folder to blob object\n await createSymlink(blobPath, pointerPath);\n return pointerPath;\n }\n\n const incomplete = `${blobPath}.incomplete`;\n logger.debug({ path: params.path, incomplete }, 'Starting file download');\n\n // Use enhanced download with retry - use branch HEAD commit for download\n const blob: Blob | null = await downloadFile({\n ...params,\n revision: branchHeadCommit,\n });\n\n if (!blob) {\n const error = `invalid response for file ${params.path}`;\n logger.error({ path: params.path }, error);\n throw new Error(error);\n }\n\n logger.debug({ size: blob.size }, 'Writing blob to disk');\n await pipeline(Readable.fromWeb(blob.stream() as ReadableStream), createWriteStream(incomplete));\n\n // rename .incomplete file to expected blob\n await rename(incomplete, blobPath);\n logger.debug({ blobPath }, 'Renamed incomplete file to final blob');\n\n // create symlinks in snapshot folder to blob object\n await createSymlink(blobPath, pointerPath);\n logger.debug({ blobPath, pointerPath }, 'Created symlink from snapshot to blob');\n\n await saveRevisionMapping({\n storageFolder,\n revision,\n commitHash: branchHeadCommit,\n });\n\n logger.debug({ pointerPath, size: blob.size }, 'File download completed successfully');\n return pointerPath;\n}\n"],"mappings":"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","names":["import_promises","pointerPath"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Fixed version of HuggingFace's downloadFileToCacheDir that matches Python's behavior
3
+ *
4
+ * Key fix: Uses branch/tag HEAD commit for snapshot paths, not file's last commit
5
+ * This ensures all files from the same revision end up in the same snapshot folder
6
+ */
7
+ import type { RepoDesignation } from '@huggingface/hub';
8
+ interface CredentialsParams {
9
+ accessToken?: string;
10
+ }
11
+ export declare const REGEX_COMMIT_HASH: RegExp;
12
+ /**
13
+ * Download a given file if it's not already present in the local cache.
14
+ * Matches Python's hf_hub_download behavior by using branch HEAD commits.
15
+ */
16
+ export declare function downloadFileToCacheDir(params: {
17
+ repo: RepoDesignation;
18
+ path: string;
19
+ /**
20
+ * If true, will download the raw git file.
21
+ */
22
+ raw?: boolean;
23
+ /**
24
+ * An optional Git revision id which can be a branch name, a tag, or a commit hash.
25
+ * @default "main"
26
+ */
27
+ revision?: string;
28
+ hubUrl?: string;
29
+ cacheDir?: string;
30
+ /**
31
+ * Custom fetch function to use instead of the default one
32
+ */
33
+ fetch?: typeof fetch;
34
+ /**
35
+ * If true, only return cached files, don't download
36
+ */
37
+ localFileOnly?: boolean;
38
+ } & Partial<CredentialsParams>): Promise<string>;
39
+ export {};
40
+ //# sourceMappingURL=hf_utils.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Fixed version of HuggingFace's downloadFileToCacheDir that matches Python's behavior
3
+ *
4
+ * Key fix: Uses branch/tag HEAD commit for snapshot paths, not file's last commit
5
+ * This ensures all files from the same revision end up in the same snapshot folder
6
+ */
7
+ import type { RepoDesignation } from '@huggingface/hub';
8
+ interface CredentialsParams {
9
+ accessToken?: string;
10
+ }
11
+ export declare const REGEX_COMMIT_HASH: RegExp;
12
+ /**
13
+ * Download a given file if it's not already present in the local cache.
14
+ * Matches Python's hf_hub_download behavior by using branch HEAD commits.
15
+ */
16
+ export declare function downloadFileToCacheDir(params: {
17
+ repo: RepoDesignation;
18
+ path: string;
19
+ /**
20
+ * If true, will download the raw git file.
21
+ */
22
+ raw?: boolean;
23
+ /**
24
+ * An optional Git revision id which can be a branch name, a tag, or a commit hash.
25
+ * @default "main"
26
+ */
27
+ revision?: string;
28
+ hubUrl?: string;
29
+ cacheDir?: string;
30
+ /**
31
+ * Custom fetch function to use instead of the default one
32
+ */
33
+ fetch?: typeof fetch;
34
+ /**
35
+ * If true, only return cached files, don't download
36
+ */
37
+ localFileOnly?: boolean;
38
+ } & Partial<CredentialsParams>): Promise<string>;
39
+ export {};
40
+ //# sourceMappingURL=hf_utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hf_utils.d.ts","sourceRoot":"","sources":["../src/hf_utils.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,OAAO,KAAK,EAAwB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAY9E,UAAU,iBAAiB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAqC,CAAC;AAgJtE;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE;IACN,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAC7B,OAAO,CAAC,MAAM,CAAC,CAsKjB"}
@@ -0,0 +1,237 @@
1
+ import { downloadFile, listCommits, pathsInfo } from "@huggingface/hub";
2
+ import { log } from "@livekit/agents";
3
+ import { createWriteStream, writeFileSync } from "node:fs";
4
+ import { lstat, mkdir, rename, stat } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import { dirname, join, relative, resolve } from "node:path";
7
+ import { Readable } from "node:stream";
8
+ import { pipeline } from "node:stream/promises";
9
+ const REGEX_COMMIT_HASH = new RegExp("^[0-9a-f]{40}$");
10
+ function getHFHubCachePath(customCacheDir) {
11
+ return customCacheDir || join(homedir(), ".cache", "huggingface", "hub");
12
+ }
13
+ function getRepoFolderName(repoId) {
14
+ return `models--${repoId.replace(/\//g, "--")}`;
15
+ }
16
+ function toRepoId(repo) {
17
+ if (typeof repo === "string") {
18
+ return repo;
19
+ }
20
+ return `${repo.name}`;
21
+ }
22
+ async function getBranchHeadCommit(repo, revision, params) {
23
+ const logger = log();
24
+ try {
25
+ if (REGEX_COMMIT_HASH.test(revision)) {
26
+ return revision;
27
+ }
28
+ for await (const commit of listCommits({
29
+ repo,
30
+ revision,
31
+ ...params
32
+ })) {
33
+ const commitHash = commit.oid || commit.id || commit.commitId;
34
+ if (commitHash) {
35
+ return commitHash;
36
+ }
37
+ break;
38
+ }
39
+ logger.error({ repo: toRepoId(repo), revision }, "No commits found for revision");
40
+ return null;
41
+ } catch (error) {
42
+ logger.error(
43
+ { error: error.message, repo: toRepoId(repo), revision },
44
+ "Error getting HEAD commit"
45
+ );
46
+ throw error;
47
+ }
48
+ }
49
+ async function createSymlink(sourcePath, targetPath) {
50
+ const logger = log();
51
+ const { symlink, rm, copyFile } = await import("node:fs/promises");
52
+ function expandUser(path) {
53
+ if (path.startsWith("~")) {
54
+ return path.replace("~", homedir());
55
+ }
56
+ return path;
57
+ }
58
+ const absSrc = resolve(expandUser(sourcePath));
59
+ const absDst = resolve(expandUser(targetPath));
60
+ try {
61
+ await rm(absDst);
62
+ } catch {
63
+ }
64
+ try {
65
+ const relativePath = relative(dirname(absDst), absSrc);
66
+ await symlink(relativePath, absDst);
67
+ logger.debug({ source: absSrc, target: absDst, relative: relativePath }, "Created symlink");
68
+ } catch (symlinkError) {
69
+ logger.warn({ source: absSrc, target: absDst }, "Symlink not supported, falling back to copy");
70
+ try {
71
+ await copyFile(absSrc, absDst);
72
+ logger.debug({ source: absSrc, target: absDst }, "File copied successfully");
73
+ } catch (copyError) {
74
+ logger.error(
75
+ { error: copyError.message, source: absSrc, target: absDst },
76
+ "Failed to copy file"
77
+ );
78
+ throw symlinkError;
79
+ }
80
+ }
81
+ }
82
+ function getFilePointer(storageFolder, revision, relativeFilename) {
83
+ const snapshotPath = join(storageFolder, "snapshots");
84
+ return join(snapshotPath, revision, relativeFilename);
85
+ }
86
+ async function exists(path, followSymlinks) {
87
+ try {
88
+ if (followSymlinks) {
89
+ await stat(path);
90
+ } else {
91
+ await lstat(path);
92
+ }
93
+ return true;
94
+ } catch (err) {
95
+ return false;
96
+ }
97
+ }
98
+ async function saveRevisionMapping({
99
+ storageFolder,
100
+ revision,
101
+ commitHash
102
+ }) {
103
+ if (!REGEX_COMMIT_HASH.test(revision) && revision !== commitHash) {
104
+ const refsPath = join(storageFolder, "refs");
105
+ await mkdir(refsPath, { recursive: true });
106
+ writeFileSync(join(refsPath, revision), commitHash);
107
+ }
108
+ }
109
+ async function downloadFileToCacheDir(params) {
110
+ const logger = log();
111
+ const revision = params.revision ?? "main";
112
+ const cacheDir = params.cacheDir ?? getHFHubCachePath();
113
+ const repoId = toRepoId(params.repo);
114
+ const storageFolder = join(cacheDir, getRepoFolderName(repoId));
115
+ let branchHeadCommit;
116
+ if (REGEX_COMMIT_HASH.test(revision)) {
117
+ branchHeadCommit = revision;
118
+ const pointerPath2 = getFilePointer(storageFolder, revision, params.path);
119
+ if (await exists(pointerPath2, true)) {
120
+ logger.debug(
121
+ { pointerPath: pointerPath2, commitHash: branchHeadCommit },
122
+ "File found in cache (commit hash)"
123
+ );
124
+ return pointerPath2;
125
+ }
126
+ }
127
+ if (params.localFileOnly) {
128
+ logger.debug({ repoId, path: params.path, revision }, "Local file only mode - checking cache");
129
+ const directPath = getFilePointer(storageFolder, revision, params.path);
130
+ if (await exists(directPath, true)) {
131
+ logger.debug({ directPath }, "File found in cache (direct path)");
132
+ return directPath;
133
+ }
134
+ if (!REGEX_COMMIT_HASH.test(revision)) {
135
+ const refsPath = join(storageFolder, "refs", revision);
136
+ try {
137
+ const { readFileSync } = await import("fs");
138
+ const resolvedHash = readFileSync(refsPath, "utf-8").trim();
139
+ const resolvedPath = getFilePointer(storageFolder, resolvedHash, params.path);
140
+ if (await exists(resolvedPath, true)) {
141
+ logger.debug({ resolvedPath, resolvedHash }, "File found in cache (via refs)");
142
+ return resolvedPath;
143
+ }
144
+ } catch {
145
+ logger.debug({ revision }, "No ref mapping found for revision");
146
+ }
147
+ }
148
+ const error = `File not found in cache: ${repoId}/${params.path} (revision: ${revision}). Make sure to run the download-files command before running the agent worker.`;
149
+ logger.error({ repoId, path: params.path, revision }, error);
150
+ throw new Error(error);
151
+ }
152
+ if (!branchHeadCommit) {
153
+ const headCommit = await getBranchHeadCommit(params.repo, revision, params);
154
+ if (!headCommit) {
155
+ throw new Error(`Failed to resolve revision ${revision} to commit hash`);
156
+ }
157
+ branchHeadCommit = headCommit;
158
+ }
159
+ const pointerPath = getFilePointer(storageFolder, branchHeadCommit, params.path);
160
+ if (await exists(pointerPath, true)) {
161
+ logger.debug({ pointerPath, branchHeadCommit }, "File found in cache (branch HEAD)");
162
+ await saveRevisionMapping({
163
+ storageFolder,
164
+ revision,
165
+ commitHash: branchHeadCommit
166
+ });
167
+ return pointerPath;
168
+ }
169
+ logger.debug(
170
+ { repoId, path: params.path, revision: branchHeadCommit },
171
+ "Fetching path info from HF API"
172
+ );
173
+ const pathsInformation = await pathsInfo({
174
+ ...params,
175
+ paths: [params.path],
176
+ revision: branchHeadCommit,
177
+ // Use HEAD commit for consistency
178
+ expand: true
179
+ });
180
+ if (!pathsInformation || pathsInformation.length !== 1) {
181
+ const error = `cannot get path info for ${params.path}`;
182
+ logger.error({ repoId, path: params.path, pathsInfoLength: pathsInformation == null ? void 0 : pathsInformation.length }, error);
183
+ throw new Error(error);
184
+ }
185
+ const pathInfo = pathsInformation[0];
186
+ if (!pathInfo) {
187
+ const error = `No path info returned for ${params.path}`;
188
+ logger.error({ repoId, path: params.path }, error);
189
+ throw new Error(error);
190
+ }
191
+ let etag;
192
+ if (pathInfo.lfs) {
193
+ etag = pathInfo.lfs.oid;
194
+ logger.debug({ etag, path: params.path }, "File is LFS pointer");
195
+ } else {
196
+ etag = pathInfo.oid;
197
+ logger.debug({ etag, path: params.path }, "File is regular git object");
198
+ }
199
+ const blobPath = join(storageFolder, "blobs", etag);
200
+ logger.debug({ branchHeadCommit, pointerPath, blobPath }, "Computed cache paths");
201
+ await mkdir(dirname(blobPath), { recursive: true });
202
+ await mkdir(dirname(pointerPath), { recursive: true });
203
+ if (await exists(blobPath)) {
204
+ logger.debug({ blobPath, etag }, "Blob already exists in cache, creating symlink only");
205
+ await createSymlink(blobPath, pointerPath);
206
+ return pointerPath;
207
+ }
208
+ const incomplete = `${blobPath}.incomplete`;
209
+ logger.debug({ path: params.path, incomplete }, "Starting file download");
210
+ const blob = await downloadFile({
211
+ ...params,
212
+ revision: branchHeadCommit
213
+ });
214
+ if (!blob) {
215
+ const error = `invalid response for file ${params.path}`;
216
+ logger.error({ path: params.path }, error);
217
+ throw new Error(error);
218
+ }
219
+ logger.debug({ size: blob.size }, "Writing blob to disk");
220
+ await pipeline(Readable.fromWeb(blob.stream()), createWriteStream(incomplete));
221
+ await rename(incomplete, blobPath);
222
+ logger.debug({ blobPath }, "Renamed incomplete file to final blob");
223
+ await createSymlink(blobPath, pointerPath);
224
+ logger.debug({ blobPath, pointerPath }, "Created symlink from snapshot to blob");
225
+ await saveRevisionMapping({
226
+ storageFolder,
227
+ revision,
228
+ commitHash: branchHeadCommit
229
+ });
230
+ logger.debug({ pointerPath, size: blob.size }, "File download completed successfully");
231
+ return pointerPath;
232
+ }
233
+ export {
234
+ REGEX_COMMIT_HASH,
235
+ downloadFileToCacheDir
236
+ };
237
+ //# sourceMappingURL=hf_utils.js.map