@dbx-tools/appkit-mastra 0.9.60 → 0.9.64
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/README.md +33 -4
- package/index.ts +6 -3
- package/lib/index.d.ts +6 -3
- package/lib/index.js +5 -3
- package/lib/src/agents.js +26 -3
- package/lib/src/config.d.ts +19 -0
- package/lib/src/config.js +9 -1
- package/lib/src/genie.js +3 -1
- package/lib/src/mlflow.d.ts +28 -4
- package/lib/src/mlflow.js +125 -14
- package/lib/src/server.js +3 -1
- package/lib/src/skill-cache.d.ts +70 -0
- package/lib/src/skill-cache.js +194 -0
- package/lib/src/telemetry.d.ts +41 -0
- package/lib/src/telemetry.js +89 -4
- package/lib/src/workspaces.d.ts +10 -0
- package/lib/src/workspaces.js +84 -24
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -14
- package/src/agents.ts +29 -2
- package/src/config.ts +30 -0
- package/src/genie.ts +2 -0
- package/src/mlflow.ts +131 -15
- package/src/server.ts +1 -0
- package/src/skill-cache.ts +312 -0
- package/src/telemetry.ts +90 -2
- package/src/workspaces.ts +107 -22
package/src/workspaces.ts
CHANGED
|
@@ -34,10 +34,17 @@ import {
|
|
|
34
34
|
type WorkspaceSandboxResolver,
|
|
35
35
|
} from "@mastra/core/workspace";
|
|
36
36
|
|
|
37
|
-
import {
|
|
38
|
-
|
|
37
|
+
import {
|
|
38
|
+
MASTRA_SCOPES_KEY,
|
|
39
|
+
MASTRA_USER_EMAIL_KEY,
|
|
40
|
+
MASTRA_USER_KEY,
|
|
41
|
+
resolveUserKey,
|
|
42
|
+
type User,
|
|
43
|
+
} from "./config.ts";
|
|
44
|
+
import { scratchFilesystem } from "./filesystems.ts";
|
|
39
45
|
import { MontySandbox } from "./monty-sandbox.ts";
|
|
40
46
|
import { DatabricksSandbox, type DatabricksWorkspaceSandboxOptions } from "./sandbox.ts";
|
|
47
|
+
import { cachedWorkspaceSkillMount, DEFAULT_WORKSPACE_SKILL_CACHE_TTL_MS } from "./skill-cache.ts";
|
|
41
48
|
import { ASSISTANT_SHARED_SKILLS_PATH, userAssistantSkillsPath } from "./skill-paths.ts";
|
|
42
49
|
|
|
43
50
|
/* ------------------------------ constants ------------------------------ */
|
|
@@ -46,6 +53,8 @@ import { ASSISTANT_SHARED_SKILLS_PATH, userAssistantSkillsPath } from "./skill-p
|
|
|
46
53
|
const WORKSPACE_FILE_SCOPES = ["workspace", "workspace.workspace", "all-apis"] as const;
|
|
47
54
|
|
|
48
55
|
const logger = log.logger("mastra/workspaces");
|
|
56
|
+
const MAX_SCOPED_WORKSPACE_FILESYSTEMS = 16;
|
|
57
|
+
const scopedWorkspaceFilesystems = new Map<string, WorkspaceFilesystem>();
|
|
49
58
|
|
|
50
59
|
/* -------------------------------- types -------------------------------- */
|
|
51
60
|
|
|
@@ -93,6 +102,11 @@ export interface WorkspaceMountContribution {
|
|
|
93
102
|
mounts: Record<string, WorkspaceFilesystem>;
|
|
94
103
|
/** Paths within the composite namespace where `SKILL.md` files are scanned. */
|
|
95
104
|
skillPaths?: string[];
|
|
105
|
+
/**
|
|
106
|
+
* Stable identity for this contribution. When every non-empty contribution
|
|
107
|
+
* supplies one, Mastra reuses the resolved source across requests.
|
|
108
|
+
*/
|
|
109
|
+
cacheKey?: string;
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
/** Contributes filesystem mounts (and optional skill paths) for one request. */
|
|
@@ -139,6 +153,11 @@ export interface CreateWorkspaceOptions {
|
|
|
139
153
|
skills?: SkillsResolver;
|
|
140
154
|
/** Forwarded to Mastra when skill discovery is enabled. */
|
|
141
155
|
checkSkillFileMtime?: boolean;
|
|
156
|
+
/**
|
|
157
|
+
* AppKit cache TTL for Databricks skill metadata and content.
|
|
158
|
+
* Defaults to five minutes.
|
|
159
|
+
*/
|
|
160
|
+
workspaceSkillRefreshTtlMs?: number;
|
|
142
161
|
/** Enable BM25 keyword search over indexed workspace content. */
|
|
143
162
|
bm25?: boolean;
|
|
144
163
|
/**
|
|
@@ -220,7 +239,11 @@ export function createWorkspace(options: CreateWorkspaceOptions = {}): Workspace
|
|
|
220
239
|
const { id, name } = resolveWorkspaceIdentity(options);
|
|
221
240
|
const skillFolders = resolveSkillFolders(options);
|
|
222
241
|
const folderNames = Object.keys(skillFolders);
|
|
223
|
-
const resolvers = buildMountResolvers(
|
|
242
|
+
const resolvers = buildMountResolvers(
|
|
243
|
+
skillFolders,
|
|
244
|
+
options.mounts,
|
|
245
|
+
options.workspaceSkillRefreshTtlMs,
|
|
246
|
+
);
|
|
224
247
|
const extraSkillPaths = options.extraSkillPaths ?? [];
|
|
225
248
|
const skills =
|
|
226
249
|
options.skills ??
|
|
@@ -238,6 +261,8 @@ export function createWorkspace(options: CreateWorkspaceOptions = {}): Workspace
|
|
|
238
261
|
customMountResolvers: options.mounts?.length ?? 0,
|
|
239
262
|
customSkillsResolver: Boolean(options.skills),
|
|
240
263
|
checkSkillFileMtime,
|
|
264
|
+
workspaceSkillRefreshTtlMs:
|
|
265
|
+
options.workspaceSkillRefreshTtlMs ?? DEFAULT_WORKSPACE_SKILL_CACHE_TTL_MS,
|
|
241
266
|
bm25,
|
|
242
267
|
extraSkillPaths: extraSkillPaths.length,
|
|
243
268
|
sandbox: sandbox ? sandboxName(options.sandbox) : "disabled",
|
|
@@ -372,9 +397,11 @@ function hasWorkspaceFileScope(requestContext: RequestContext | undefined): bool
|
|
|
372
397
|
async function resolveSkillFolderMounts(
|
|
373
398
|
skillFolders: Record<string, SkillFolderOptions>,
|
|
374
399
|
context: WorkspaceMountContext,
|
|
400
|
+
workspaceSkillRefreshTtlMs: number,
|
|
375
401
|
): Promise<WorkspaceMountContribution> {
|
|
376
402
|
const mounts: Record<string, WorkspaceFilesystem> = {};
|
|
377
403
|
const skillPaths: string[] = [];
|
|
404
|
+
const cacheKeys: string[] = [];
|
|
378
405
|
const requestContext = context.requestContext;
|
|
379
406
|
|
|
380
407
|
if (!requestContext || !shouldMountSkillFolders(requestContext)) {
|
|
@@ -388,12 +415,20 @@ async function resolveSkillFolderMounts(
|
|
|
388
415
|
|
|
389
416
|
const user = requestContext.get(MASTRA_USER_KEY) as User | undefined;
|
|
390
417
|
const client = user?.executionContext.client as WorkspaceClient | undefined;
|
|
418
|
+
const userKey = user ? resolveUserKey(requestContext) : undefined;
|
|
419
|
+
const host = client ? (await client.config.getHost()).toString() : undefined;
|
|
391
420
|
|
|
392
421
|
for (const [name, folder] of Object.entries(skillFolders)) {
|
|
393
|
-
const
|
|
394
|
-
|
|
422
|
+
const resolved = await resolveSkillFolderFilesystem(name, folder, context, {
|
|
423
|
+
client,
|
|
424
|
+
host,
|
|
425
|
+
userKey,
|
|
426
|
+
workspaceSkillRefreshTtlMs,
|
|
427
|
+
});
|
|
428
|
+
if (!resolved) continue;
|
|
395
429
|
const mount = folder.mount ?? `/${name}`;
|
|
396
|
-
mounts[mount] = filesystem;
|
|
430
|
+
mounts[mount] = resolved.filesystem;
|
|
431
|
+
if (resolved.cacheKey) cacheKeys.push(resolved.cacheKey);
|
|
397
432
|
if (folder.readable !== false) skillPaths.push(mount);
|
|
398
433
|
}
|
|
399
434
|
|
|
@@ -402,7 +437,13 @@ async function resolveSkillFolderMounts(
|
|
|
402
437
|
skillPaths,
|
|
403
438
|
});
|
|
404
439
|
|
|
405
|
-
return {
|
|
440
|
+
return {
|
|
441
|
+
mounts,
|
|
442
|
+
skillPaths,
|
|
443
|
+
...(cacheKeys.length === Object.keys(mounts).length && cacheKeys.length > 0
|
|
444
|
+
? { cacheKey: object.toStableKey(cacheKeys.sort()) }
|
|
445
|
+
: {}),
|
|
446
|
+
};
|
|
406
447
|
}
|
|
407
448
|
|
|
408
449
|
/**
|
|
@@ -413,13 +454,19 @@ async function resolveSkillFolderFilesystem(
|
|
|
413
454
|
name: string,
|
|
414
455
|
folder: SkillFolderOptions,
|
|
415
456
|
context: WorkspaceMountContext,
|
|
416
|
-
|
|
417
|
-
|
|
457
|
+
cache: {
|
|
458
|
+
client: WorkspaceClient | undefined;
|
|
459
|
+
host: string | undefined;
|
|
460
|
+
userKey: string | undefined;
|
|
461
|
+
workspaceSkillRefreshTtlMs: number;
|
|
462
|
+
},
|
|
463
|
+
): Promise<{ cacheKey?: string; filesystem: WorkspaceFilesystem } | undefined> {
|
|
418
464
|
if (folder.filesystem !== undefined) {
|
|
419
|
-
|
|
465
|
+
const filesystem = await resolveSkillFolderValue(folder.filesystem, context);
|
|
466
|
+
return filesystem ? { filesystem } : undefined;
|
|
420
467
|
}
|
|
421
468
|
// A path mount needs the request's OBO client to reach the workspace.
|
|
422
|
-
if (folder.path === undefined || !client) {
|
|
469
|
+
if (folder.path === undefined || !cache.client || !cache.host || !cache.userKey) {
|
|
423
470
|
logger.debug("skill-folder:skipped", {
|
|
424
471
|
name,
|
|
425
472
|
reason: folder.path === undefined ? "no-location" : "missing-obo-client",
|
|
@@ -428,7 +475,11 @@ async function resolveSkillFolderFilesystem(
|
|
|
428
475
|
}
|
|
429
476
|
const root = stringUtils.trimToNull(await resolveSkillFolderValue(folder.path, context));
|
|
430
477
|
if (!root) return undefined;
|
|
431
|
-
return databricksFilesystem(client, root, folder.writable !== true
|
|
478
|
+
return databricksFilesystem(cache.client, root, folder.writable !== true, {
|
|
479
|
+
host: cache.host,
|
|
480
|
+
ttlMs: cache.workspaceSkillRefreshTtlMs,
|
|
481
|
+
userKey: cache.userKey,
|
|
482
|
+
});
|
|
432
483
|
}
|
|
433
484
|
|
|
434
485
|
/** Read a {@link SkillFolderValue}, calling it when it is a per-request resolver. */
|
|
@@ -464,11 +515,14 @@ function resolveWorkspaceIdentity(options: CreateWorkspaceOptions): {
|
|
|
464
515
|
function buildMountResolvers(
|
|
465
516
|
skillFolders: Record<string, SkillFolderOptions>,
|
|
466
517
|
mounts: WorkspaceMountResolver[] | undefined,
|
|
518
|
+
workspaceSkillRefreshTtlMs = DEFAULT_WORKSPACE_SKILL_CACHE_TTL_MS,
|
|
467
519
|
): WorkspaceMountResolver[] {
|
|
468
520
|
const resolvers: WorkspaceMountResolver[] = [];
|
|
469
521
|
const folderCount = Object.keys(skillFolders).length;
|
|
470
522
|
if (folderCount > 0) {
|
|
471
|
-
resolvers.push((context) =>
|
|
523
|
+
resolvers.push((context) =>
|
|
524
|
+
resolveSkillFolderMounts(skillFolders, context, workspaceSkillRefreshTtlMs),
|
|
525
|
+
);
|
|
472
526
|
}
|
|
473
527
|
if (mounts?.length) {
|
|
474
528
|
resolvers.push(...mounts);
|
|
@@ -506,18 +560,23 @@ async function databricksFilesystem(
|
|
|
506
560
|
client: WorkspaceClient,
|
|
507
561
|
root: string,
|
|
508
562
|
readOnly: boolean = true,
|
|
509
|
-
|
|
563
|
+
cache: { host: string; ttlMs: number; userKey: string },
|
|
564
|
+
): Promise<{ cacheKey: string; filesystem: WorkspaceFilesystem } | undefined> {
|
|
510
565
|
const fs = new DatabricksFileSystem({
|
|
511
566
|
client: workspaceClient.toLegacyWorkspaceClient(client),
|
|
512
567
|
root,
|
|
513
568
|
readOnly,
|
|
514
569
|
createRoot: !readOnly,
|
|
515
570
|
});
|
|
571
|
+
const cached = cachedWorkspaceSkillMount({
|
|
572
|
+
host: cache.host,
|
|
573
|
+
source: fs,
|
|
574
|
+
ttlMs: cache.ttlMs,
|
|
575
|
+
userKey: cache.userKey,
|
|
576
|
+
});
|
|
516
577
|
try {
|
|
517
|
-
await
|
|
518
|
-
if (await
|
|
519
|
-
return filesystems(fs, { readOnly });
|
|
520
|
-
}
|
|
578
|
+
await cached.filesystem.init();
|
|
579
|
+
if (await cached.filesystem.exists(".")) return cached;
|
|
521
580
|
} catch (err) {
|
|
522
581
|
logger.debug("databricks-mount:scratch-fallback", {
|
|
523
582
|
root,
|
|
@@ -525,7 +584,7 @@ async function databricksFilesystem(
|
|
|
525
584
|
error: errorUtils.errorMessage(err),
|
|
526
585
|
});
|
|
527
586
|
}
|
|
528
|
-
return
|
|
587
|
+
return undefined;
|
|
529
588
|
}
|
|
530
589
|
|
|
531
590
|
/**
|
|
@@ -538,6 +597,8 @@ async function resolveWorkspaceContribution(
|
|
|
538
597
|
): Promise<WorkspaceMountContribution> {
|
|
539
598
|
const mounts: Record<string, WorkspaceFilesystem> = {};
|
|
540
599
|
const skillPaths: string[] = [];
|
|
600
|
+
const cacheKeys: string[] = [];
|
|
601
|
+
let cacheable = true;
|
|
541
602
|
|
|
542
603
|
for (const [index, resolver] of resolvers.entries()) {
|
|
543
604
|
const contribution = await resolver(context);
|
|
@@ -547,6 +608,10 @@ async function resolveWorkspaceContribution(
|
|
|
547
608
|
skillPaths: contribution.skillPaths ?? [],
|
|
548
609
|
});
|
|
549
610
|
Object.assign(mounts, contribution.mounts);
|
|
611
|
+
if (Object.keys(contribution.mounts).length > 0) {
|
|
612
|
+
if (contribution.cacheKey) cacheKeys.push(contribution.cacheKey);
|
|
613
|
+
else cacheable = false;
|
|
614
|
+
}
|
|
550
615
|
if (contribution.skillPaths?.length) {
|
|
551
616
|
skillPaths.push(...contribution.skillPaths);
|
|
552
617
|
}
|
|
@@ -557,7 +622,13 @@ async function resolveWorkspaceContribution(
|
|
|
557
622
|
skillPaths,
|
|
558
623
|
});
|
|
559
624
|
|
|
560
|
-
return {
|
|
625
|
+
return {
|
|
626
|
+
mounts,
|
|
627
|
+
skillPaths,
|
|
628
|
+
...(cacheable && cacheKeys.length > 0
|
|
629
|
+
? { cacheKey: object.toStableKey(cacheKeys.sort()) }
|
|
630
|
+
: {}),
|
|
631
|
+
};
|
|
561
632
|
}
|
|
562
633
|
|
|
563
634
|
/**
|
|
@@ -570,7 +641,7 @@ async function resolveWorkspaceFilesystem(
|
|
|
570
641
|
resolvers: WorkspaceMountResolver[],
|
|
571
642
|
context: WorkspaceMountContext,
|
|
572
643
|
): Promise<WorkspaceFilesystem> {
|
|
573
|
-
const { mounts } = await resolveWorkspaceContribution(resolvers, context);
|
|
644
|
+
const { cacheKey, mounts } = await resolveWorkspaceContribution(resolvers, context);
|
|
574
645
|
const mountKeys = Object.keys(mounts);
|
|
575
646
|
if (mountKeys.length === 0) {
|
|
576
647
|
logger.debug("filesystem:scratch", {
|
|
@@ -579,7 +650,21 @@ async function resolveWorkspaceFilesystem(
|
|
|
579
650
|
return scratchFilesystem();
|
|
580
651
|
}
|
|
581
652
|
logger.debug("filesystem:composite", { mountKeys });
|
|
582
|
-
return new CompositeFilesystem({ mounts });
|
|
653
|
+
if (!cacheKey) return new CompositeFilesystem({ mounts });
|
|
654
|
+
const existing = scopedWorkspaceFilesystems.get(cacheKey);
|
|
655
|
+
if (existing) {
|
|
656
|
+
scopedWorkspaceFilesystems.delete(cacheKey);
|
|
657
|
+
scopedWorkspaceFilesystems.set(cacheKey, existing);
|
|
658
|
+
return existing;
|
|
659
|
+
}
|
|
660
|
+
const filesystem = new CompositeFilesystem({ mounts });
|
|
661
|
+
scopedWorkspaceFilesystems.set(cacheKey, filesystem);
|
|
662
|
+
while (scopedWorkspaceFilesystems.size > MAX_SCOPED_WORKSPACE_FILESYSTEMS) {
|
|
663
|
+
const oldest = scopedWorkspaceFilesystems.keys().next().value;
|
|
664
|
+
if (oldest === undefined) break;
|
|
665
|
+
scopedWorkspaceFilesystems.delete(oldest);
|
|
666
|
+
}
|
|
667
|
+
return filesystem;
|
|
583
668
|
}
|
|
584
669
|
|
|
585
670
|
/**
|