@dbx-tools/appkit-mastra 0.9.74 → 0.9.79
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 +53 -42
- package/index.ts +8 -3
- package/lib/index.d.ts +8 -3
- package/lib/index.js +6 -3
- package/lib/src/agents.js +38 -5
- package/lib/src/config.d.ts +7 -7
- package/lib/src/config.js +4 -4
- package/lib/src/genie.js +9 -15
- package/lib/src/mlflow.js +5 -5
- package/lib/src/monty-native.d.ts +40 -0
- package/lib/src/monty-native.js +100 -0
- package/lib/src/monty-sandbox.js +7 -3
- package/lib/src/remote-skills.js +26 -2
- package/lib/src/skill-cache.d.ts +80 -56
- package/lib/src/skill-cache.js +215 -158
- package/lib/src/skill-search.d.ts +30 -0
- package/lib/src/skill-search.js +171 -0
- package/lib/src/workspaces.d.ts +10 -5
- package/lib/src/workspaces.js +132 -117
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -15
- package/src/agents.ts +39 -6
- package/src/config.ts +10 -10
- package/src/genie.ts +8 -13
- package/src/mlflow.ts +4 -4
- package/src/monty-native.ts +149 -0
- package/src/monty-sandbox.ts +7 -2
- package/src/remote-skills.ts +32 -1
- package/src/skill-cache.ts +278 -243
- package/src/skill-search.ts +203 -0
- package/src/workspaces.ts +177 -139
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/agents.ts","../src/chart.ts","../src/chat.ts","../src/config.ts","../src/defaults.ts","../src/evaluation.ts","../src/filesystems.ts","../src/genie.ts","../src/identity.ts","../src/mcp.ts","../src/memory.ts","../src/mlflow.ts","../src/model.ts","../src/monty-sandbox.ts","../src/observability.ts","../src/openapi-tool.ts","../src/plugin.ts","../src/processors.ts","../src/remote-skills.ts","../src/rest.ts","../src/sandbox.ts","../src/server.ts","../src/serving-sanitize.ts","../src/serving.ts","../src/skill-cache.ts","../src/skill-paths.ts","../src/statement.ts","../src/storage-schema.ts","../src/style.ts","../src/summarize.ts","../src/telemetry.ts","../src/trace-attributes.ts","../src/validation.ts","../src/workspaces.ts","../src/writer.ts","../index.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/agents.ts","../src/chart.ts","../src/chat.ts","../src/config.ts","../src/defaults.ts","../src/evaluation.ts","../src/filesystems.ts","../src/genie.ts","../src/identity.ts","../src/mcp.ts","../src/memory.ts","../src/mlflow.ts","../src/model.ts","../src/monty-native.ts","../src/monty-sandbox.ts","../src/observability.ts","../src/openapi-tool.ts","../src/plugin.ts","../src/processors.ts","../src/remote-skills.ts","../src/rest.ts","../src/sandbox.ts","../src/server.ts","../src/serving-sanitize.ts","../src/serving.ts","../src/skill-cache.ts","../src/skill-paths.ts","../src/skill-search.ts","../src/statement.ts","../src/storage-schema.ts","../src/style.ts","../src/summarize.ts","../src/telemetry.ts","../src/trace-attributes.ts","../src/validation.ts","../src/workspaces.ts","../src/writer.ts","../index.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@databricks/appkit": "0.81.0",
|
|
37
|
-
"@dbx-tools/appkit": "^0.9.
|
|
38
|
-
"@dbx-tools/core": "^0.9.
|
|
39
|
-
"@dbx-tools/databricks": "^0.9.
|
|
40
|
-
"@dbx-tools/fs": "^0.9.
|
|
41
|
-
"@dbx-tools/genie": "^0.9.
|
|
42
|
-
"@dbx-tools/model": "^0.9.
|
|
43
|
-
"@dbx-tools/path": "^0.9.
|
|
44
|
-
"@dbx-tools/postgres": "^0.9.
|
|
45
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
46
|
-
"@dbx-tools/shared-fs": "^0.9.
|
|
47
|
-
"@dbx-tools/shared-genie": "^0.9.
|
|
48
|
-
"@dbx-tools/shared-mastra": "^0.9.
|
|
49
|
-
"@dbx-tools/shared-model": "^0.9.
|
|
37
|
+
"@dbx-tools/appkit": "^0.9.79",
|
|
38
|
+
"@dbx-tools/core": "^0.9.79",
|
|
39
|
+
"@dbx-tools/databricks": "^0.9.79",
|
|
40
|
+
"@dbx-tools/fs": "^0.9.79",
|
|
41
|
+
"@dbx-tools/genie": "^0.9.79",
|
|
42
|
+
"@dbx-tools/model": "^0.9.79",
|
|
43
|
+
"@dbx-tools/path": "^0.9.79",
|
|
44
|
+
"@dbx-tools/postgres": "^0.9.79",
|
|
45
|
+
"@dbx-tools/shared-core": "^0.9.79",
|
|
46
|
+
"@dbx-tools/shared-fs": "^0.9.79",
|
|
47
|
+
"@dbx-tools/shared-genie": "^0.9.79",
|
|
48
|
+
"@dbx-tools/shared-mastra": "^0.9.79",
|
|
49
|
+
"@dbx-tools/shared-model": "^0.9.79",
|
|
50
50
|
"@mastra/ai-sdk": "1.10.5",
|
|
51
51
|
"@mastra/core": "1.71.0",
|
|
52
52
|
"@mastra/express": "1.5.15",
|
|
@@ -61,9 +61,12 @@
|
|
|
61
61
|
"@opentelemetry/core": "2.11.0",
|
|
62
62
|
"@opentelemetry/sdk-trace-base": "2.8.0",
|
|
63
63
|
"@opentelemetry/sdk-trace-node": "2.8.0",
|
|
64
|
-
"@pydantic/monty": "0.0
|
|
64
|
+
"@pydantic/monty": "1.0.0",
|
|
65
65
|
"express": "^5.1.0",
|
|
66
|
+
"fuse.js": "^7.4.2",
|
|
67
|
+
"lru-cache": "11.5.3",
|
|
66
68
|
"pg": "^8.22.0",
|
|
69
|
+
"yaml": "^2.9.0",
|
|
67
70
|
"zod": "4.3.6"
|
|
68
71
|
},
|
|
69
72
|
"main": "./lib/index.js",
|
|
@@ -129,6 +132,10 @@
|
|
|
129
132
|
"types": "./lib/src/model.d.ts",
|
|
130
133
|
"default": "./lib/src/model.js"
|
|
131
134
|
},
|
|
135
|
+
"./monty-native": {
|
|
136
|
+
"types": "./lib/src/monty-native.d.ts",
|
|
137
|
+
"default": "./lib/src/monty-native.js"
|
|
138
|
+
},
|
|
132
139
|
"./monty-sandbox": {
|
|
133
140
|
"types": "./lib/src/monty-sandbox.d.ts",
|
|
134
141
|
"default": "./lib/src/monty-sandbox.js"
|
|
@@ -181,6 +188,10 @@
|
|
|
181
188
|
"types": "./lib/src/skill-paths.d.ts",
|
|
182
189
|
"default": "./lib/src/skill-paths.js"
|
|
183
190
|
},
|
|
191
|
+
"./skill-search": {
|
|
192
|
+
"types": "./lib/src/skill-search.d.ts",
|
|
193
|
+
"default": "./lib/src/skill-search.js"
|
|
194
|
+
},
|
|
184
195
|
"./statement": {
|
|
185
196
|
"types": "./lib/src/statement.d.ts",
|
|
186
197
|
"default": "./lib/src/statement.js"
|
|
@@ -220,7 +231,7 @@
|
|
|
220
231
|
"./package.json": "./package.json"
|
|
221
232
|
}
|
|
222
233
|
},
|
|
223
|
-
"version": "0.9.
|
|
234
|
+
"version": "0.9.79",
|
|
224
235
|
"types": "./lib/index.d.ts",
|
|
225
236
|
"dbxToolsConfig": {
|
|
226
237
|
"tags": [
|
|
@@ -285,6 +296,10 @@
|
|
|
285
296
|
"types": "./lib/src/model.d.ts",
|
|
286
297
|
"default": "./lib/src/model.js"
|
|
287
298
|
},
|
|
299
|
+
"./monty-native": {
|
|
300
|
+
"types": "./lib/src/monty-native.d.ts",
|
|
301
|
+
"default": "./lib/src/monty-native.js"
|
|
302
|
+
},
|
|
288
303
|
"./monty-sandbox": {
|
|
289
304
|
"types": "./lib/src/monty-sandbox.d.ts",
|
|
290
305
|
"default": "./lib/src/monty-sandbox.js"
|
|
@@ -337,6 +352,10 @@
|
|
|
337
352
|
"types": "./lib/src/skill-paths.d.ts",
|
|
338
353
|
"default": "./lib/src/skill-paths.js"
|
|
339
354
|
},
|
|
355
|
+
"./skill-search": {
|
|
356
|
+
"types": "./lib/src/skill-search.d.ts",
|
|
357
|
+
"default": "./lib/src/skill-search.js"
|
|
358
|
+
},
|
|
340
359
|
"./statement": {
|
|
341
360
|
"types": "./lib/src/statement.d.ts",
|
|
342
361
|
"default": "./lib/src/statement.js"
|
package/src/agents.ts
CHANGED
|
@@ -25,7 +25,7 @@ import type {
|
|
|
25
25
|
} from "@databricks/appkit/beta";
|
|
26
26
|
import { pluginRegistry, toolkitEntries } from "@dbx-tools/appkit";
|
|
27
27
|
import type { AgentToolExecutionContext } from "@dbx-tools/appkit/tool-provider";
|
|
28
|
-
import { log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
28
|
+
import { errorUtils, log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
29
29
|
import { TOOL_PROGRESS_PART_TYPE, ToolProgressEventSchema } from "@dbx-tools/shared-mastra/wire";
|
|
30
30
|
import type {
|
|
31
31
|
AgentConfig,
|
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
} from "@mastra/core/agent";
|
|
36
36
|
import { Agent } from "@mastra/core/agent";
|
|
37
37
|
import { SkillSearchProcessor } from "@mastra/core/processors";
|
|
38
|
+
import type { OutputProcessor } from "@mastra/core/processors";
|
|
38
39
|
import { MASTRA_RESOURCE_ID_KEY } from "@mastra/core/request-context";
|
|
39
40
|
import type { Tool } from "@mastra/core/tools";
|
|
40
41
|
import { createTool } from "@mastra/core/tools";
|
|
@@ -48,9 +49,10 @@ import type { MemoryBuilder } from "./memory.ts";
|
|
|
48
49
|
import { buildModel, RESPONSES_PROVIDER_OPTIONS } from "./model.ts";
|
|
49
50
|
import { stripStaleChartsProcessor } from "./processors.ts";
|
|
50
51
|
import { MASTRA_RESOLVED_MODEL_KEY } from "./serving.ts";
|
|
52
|
+
import { CatalogueSkillSearchProcessor } from "./skill-search.ts";
|
|
51
53
|
import { TYPOGRAPHY_RULE } from "./style.ts";
|
|
52
54
|
import { buildSummarizeTool } from "./summarize.ts";
|
|
53
|
-
import { createWorkspace } from "./workspaces.ts";
|
|
55
|
+
import { createWorkspace, workspaceSkillCatalogueResolver } from "./workspaces.ts";
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* Tool record accepted by every Mastra `Agent.tools` field and by the
|
|
@@ -583,8 +585,7 @@ export async function buildAgents(opts: {
|
|
|
583
585
|
...(config.workspaceSkillRefreshTtlMs !== undefined
|
|
584
586
|
? { workspaceSkillRefreshTtlMs: config.workspaceSkillRefreshTtlMs }
|
|
585
587
|
: {}),
|
|
586
|
-
sandbox:
|
|
587
|
-
config.sandbox === undefined || config.sandbox === true ? "databricks" : config.sandbox,
|
|
588
|
+
sandbox: config.sandbox === true ? "databricks" : config.sandbox,
|
|
588
589
|
});
|
|
589
590
|
markDefaultWorkspace(workspace);
|
|
590
591
|
}
|
|
@@ -613,6 +614,7 @@ export async function buildAgents(opts: {
|
|
|
613
614
|
...(memory ? { memory } : {}),
|
|
614
615
|
...(workspace ? { workspace } : {}),
|
|
615
616
|
inputProcessors: [...inputProcessors, ...workspaceSkillInputProcessors(workspace, config)],
|
|
617
|
+
outputProcessors: [toolErrorLoggingProcessor(log)],
|
|
616
618
|
});
|
|
617
619
|
// Surface the effective default model per agent so operators can
|
|
618
620
|
// see at a glance which endpoint each agent points at without
|
|
@@ -644,13 +646,44 @@ export async function buildAgents(opts: {
|
|
|
644
646
|
return { agents, defaultAgentId, defaultModels, ambientTools };
|
|
645
647
|
}
|
|
646
648
|
|
|
649
|
+
/** Log failed tool calls without changing the chunks returned to the client. */
|
|
650
|
+
function toolErrorLoggingProcessor(logger: log.Logger): OutputProcessor {
|
|
651
|
+
return {
|
|
652
|
+
id: "tool-error-logger",
|
|
653
|
+
async processOutputStream({ part }) {
|
|
654
|
+
if (part.type === "tool-error") {
|
|
655
|
+
const error = errorUtils.toError(part.payload.error);
|
|
656
|
+
logger.error("tool execution failed", {
|
|
657
|
+
toolName: part.payload.toolName,
|
|
658
|
+
toolCallId: part.payload.toolCallId,
|
|
659
|
+
error: error.message,
|
|
660
|
+
stack: error.stack,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
return part;
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
647
668
|
function workspaceSkillInputProcessors(
|
|
648
669
|
workspace: Workspace | undefined,
|
|
649
670
|
config: MastraPluginConfig,
|
|
650
|
-
): SkillSearchProcessor
|
|
651
|
-
if (!workspace
|
|
671
|
+
): Array<SkillSearchProcessor | CatalogueSkillSearchProcessor> {
|
|
672
|
+
if (!workspace || config.workspaceSkillSearch === false) return [];
|
|
652
673
|
const options =
|
|
653
674
|
typeof config.workspaceSkillSearch === "object" ? config.workspaceSkillSearch : undefined;
|
|
675
|
+
const catalogue = workspaceSkillCatalogueResolver(workspace);
|
|
676
|
+
if (catalogue) {
|
|
677
|
+
return [
|
|
678
|
+
new CatalogueSkillSearchProcessor({
|
|
679
|
+
resolve: catalogue,
|
|
680
|
+
topK: options?.topK ?? 5,
|
|
681
|
+
minScore: options?.minScore ?? 0.1,
|
|
682
|
+
...(options?.ttlMs !== undefined ? { ttlMs: options.ttlMs } : {}),
|
|
683
|
+
}),
|
|
684
|
+
];
|
|
685
|
+
}
|
|
686
|
+
if (!workspace.skills) return [];
|
|
654
687
|
return [
|
|
655
688
|
new SkillSearchProcessor({
|
|
656
689
|
workspace,
|
package/src/config.ts
CHANGED
|
@@ -211,20 +211,20 @@ export interface MastraPluginConfig extends BasePluginConfig {
|
|
|
211
211
|
*/
|
|
212
212
|
memory?: boolean | MastraMemoryConfig;
|
|
213
213
|
/**
|
|
214
|
-
* Sandbox for auto-created agent workspaces. Defaults to
|
|
215
|
-
* `
|
|
216
|
-
*
|
|
214
|
+
* Sandbox for auto-created agent workspaces. Defaults to Monty. `true`,
|
|
215
|
+
* `"databricks"`, or an object selects/configures Databricks Sandbox, and
|
|
216
|
+
* `false` disables workspace command execution. An agent with an explicit
|
|
217
217
|
* custom `workspace` keeps that workspace and its sandbox.
|
|
218
218
|
*/
|
|
219
219
|
sandbox?: boolean | "databricks" | "monty" | DatabricksWorkspaceSandboxOptions;
|
|
220
220
|
/**
|
|
221
|
-
*
|
|
222
|
-
* Defaults to five minutes.
|
|
221
|
+
* Refresh TTL for the complete user-scoped workspace skill catalogue.
|
|
222
|
+
* Defaults to five minutes; auxiliary files remain uncached.
|
|
223
223
|
*/
|
|
224
224
|
workspaceSkillRefreshTtlMs?: number;
|
|
225
225
|
/**
|
|
226
|
-
* Use
|
|
227
|
-
*
|
|
226
|
+
* Use dbx-tools catalogue-backed on-demand skill search. Defaults to true.
|
|
227
|
+
* Pass an object to tune search.
|
|
228
228
|
*/
|
|
229
229
|
workspaceSkillSearch?: boolean | MastraSkillSearchConfig;
|
|
230
230
|
/**
|
|
@@ -665,17 +665,17 @@ export const MASTRA_CONFIG_SCHEMA: ConfigSchema = {
|
|
|
665
665
|
sandbox: {
|
|
666
666
|
type: ["boolean", "string", "object"],
|
|
667
667
|
description:
|
|
668
|
-
'Command sandbox for auto-created agent workspaces. Defaults to
|
|
668
|
+
'Command sandbox for auto-created agent workspaces. Defaults to Monty; false disables command execution, and true, "databricks", or an object selects/configures Databricks Sandbox with optional fallback.',
|
|
669
669
|
},
|
|
670
670
|
workspaceSkillRefreshTtlMs: {
|
|
671
671
|
type: "number",
|
|
672
672
|
description:
|
|
673
|
-
"
|
|
673
|
+
"Refresh TTL in milliseconds for the complete user-scoped workspace skill catalogue. Defaults to 5 minutes; auxiliary file reads stay fresh.",
|
|
674
674
|
},
|
|
675
675
|
workspaceSkillSearch: {
|
|
676
676
|
type: ["boolean", "object"],
|
|
677
677
|
description:
|
|
678
|
-
"Use
|
|
678
|
+
"Use dbx-tools catalogue-backed on-demand skill search with lazy auxiliary reads. Defaults to true.",
|
|
679
679
|
},
|
|
680
680
|
defaultAgent: {
|
|
681
681
|
type: "string",
|
package/src/genie.ts
CHANGED
|
@@ -52,6 +52,7 @@ import type { StartedEvent } from "@dbx-tools/shared-mastra";
|
|
|
52
52
|
import type { RequestContext } from "@mastra/core/request-context";
|
|
53
53
|
import { MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
|
|
54
54
|
import { createTool, type ToolExecutionContext } from "@mastra/core/tools";
|
|
55
|
+
import { LRUCache } from "lru-cache";
|
|
55
56
|
import { z } from "zod";
|
|
56
57
|
|
|
57
58
|
import type { MastraTools } from "./agents.ts";
|
|
@@ -1089,26 +1090,20 @@ const SUGGESTION_LIMIT = 6;
|
|
|
1089
1090
|
*/
|
|
1090
1091
|
const SUGGESTION_CACHE_TTL_MS = 10 * 60_000;
|
|
1091
1092
|
|
|
1092
|
-
/**
|
|
1093
|
-
const _suggestionCache = new
|
|
1093
|
+
/** Bounded process-local cache of parsed sample questions by Genie space id. */
|
|
1094
|
+
const _suggestionCache = new LRUCache<string, string[]>({
|
|
1095
|
+
max: 128,
|
|
1096
|
+
ttl: SUGGESTION_CACHE_TTL_MS,
|
|
1097
|
+
});
|
|
1094
1098
|
|
|
1095
1099
|
/** Read a space's cached questions, or `undefined` on miss / expiry. */
|
|
1096
1100
|
function readSuggestionCache(spaceId: string): string[] | undefined {
|
|
1097
|
-
|
|
1098
|
-
if (!entry) return undefined;
|
|
1099
|
-
if (entry.expires <= Date.now()) {
|
|
1100
|
-
_suggestionCache.delete(spaceId);
|
|
1101
|
-
return undefined;
|
|
1102
|
-
}
|
|
1103
|
-
return entry.questions;
|
|
1101
|
+
return _suggestionCache.get(spaceId);
|
|
1104
1102
|
}
|
|
1105
1103
|
|
|
1106
1104
|
/** Cache a space's parsed questions for {@link SUGGESTION_CACHE_TTL_MS}. */
|
|
1107
1105
|
function writeSuggestionCache(spaceId: string, questions: string[]): void {
|
|
1108
|
-
_suggestionCache.set(spaceId,
|
|
1109
|
-
questions,
|
|
1110
|
-
expires: Date.now() + SUGGESTION_CACHE_TTL_MS,
|
|
1111
|
-
});
|
|
1106
|
+
_suggestionCache.set(spaceId, questions);
|
|
1112
1107
|
}
|
|
1113
1108
|
|
|
1114
1109
|
/**
|
package/src/mlflow.ts
CHANGED
|
@@ -30,8 +30,8 @@ import { appkit } from "@dbx-tools/appkit";
|
|
|
30
30
|
import { asyncUtils, errorUtils, log, object } from "@dbx-tools/shared-core";
|
|
31
31
|
import { feedback } from "@dbx-tools/shared-mastra";
|
|
32
32
|
import { TraceLocationType, TraceMetadataKey } from "@mlflow/core";
|
|
33
|
-
import { TraceInfo } from "@mlflow/core/dist/core/entities/trace_info";
|
|
34
|
-
import { TraceState } from "@mlflow/core/dist/core/entities/trace_state";
|
|
33
|
+
import { TraceInfo } from "@mlflow/core/dist/core/entities/trace_info.js";
|
|
34
|
+
import { TraceState } from "@mlflow/core/dist/core/entities/trace_state.js";
|
|
35
35
|
import type { Context } from "@opentelemetry/api";
|
|
36
36
|
import type { ReadableSpan, Span as SdkSpan, SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
37
37
|
import type { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
@@ -455,7 +455,7 @@ async function detectedDirectMlflowTraceLocation(
|
|
|
455
455
|
const client = new mlflow.MlflowClient({ trackingUri, authProvider });
|
|
456
456
|
const experiment = await client.getExperiment(experimentId);
|
|
457
457
|
if (!experiment) return undefined;
|
|
458
|
-
const { ucLocationFromExperimentTags } = await import("@mlflow/core/dist/core/destination");
|
|
458
|
+
const { ucLocationFromExperimentTags } = await import("@mlflow/core/dist/core/destination.js");
|
|
459
459
|
const location = ucLocationFromExperimentTags(experiment.tags);
|
|
460
460
|
if (!location?.tablePrefix) return undefined;
|
|
461
461
|
return {
|
|
@@ -505,7 +505,7 @@ export async function initializeDirectMlflowTracing(): Promise<boolean> {
|
|
|
505
505
|
}
|
|
506
506
|
const [{ DatabricksUCTableSpanExporter, DatabricksUCTableSpanProcessor }, sdk] =
|
|
507
507
|
await Promise.all([
|
|
508
|
-
import("@mlflow/core/dist/exporters/uc_table"),
|
|
508
|
+
import("@mlflow/core/dist/exporters/uc_table.js"),
|
|
509
509
|
import("@opentelemetry/sdk-trace-node"),
|
|
510
510
|
]);
|
|
511
511
|
const authProvider = mlflow.createAuthProvider({
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensures Monty's platform native package is present before the Node addon loads.
|
|
3
|
+
*
|
|
4
|
+
* `@pydantic/monty` ships `@pydantic/monty-<platform>` as optionalDependencies.
|
|
5
|
+
* npm can skip those (https://github.com/npm/cli/issues/4828), which surfaces as
|
|
6
|
+
* "Cannot find native binding". This module installs only the current platform
|
|
7
|
+
* package, at the already-installed `@pydantic/monty` version, without rewriting
|
|
8
|
+
* package.json.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import { COMMAND_NOT_FOUND_EXIT_CODE, exec } from "@dbx-tools/core";
|
|
15
|
+
import { errorUtils, log } from "@dbx-tools/shared-core";
|
|
16
|
+
|
|
17
|
+
const logger = log.logger("mastra/monty-native");
|
|
18
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
/** Result of spawning the package installer. */
|
|
21
|
+
export interface MontyNativeInstallResult {
|
|
22
|
+
exitCode: number | null;
|
|
23
|
+
stdout: string;
|
|
24
|
+
stderr: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Installer used by {@link ensureMontyNativeBinding}. */
|
|
28
|
+
export type MontyNativeInstaller = (
|
|
29
|
+
command: string,
|
|
30
|
+
args: readonly string[],
|
|
31
|
+
options: { cwd: string },
|
|
32
|
+
) => Promise<MontyNativeInstallResult>;
|
|
33
|
+
|
|
34
|
+
/** Overrides for {@link ensureMontyNativeBinding} (tests inject resolve/install). */
|
|
35
|
+
export interface EnsureMontyNativeBindingOptions {
|
|
36
|
+
/** Working directory for the in-place install. Defaults to `process.cwd()`. */
|
|
37
|
+
cwd?: string;
|
|
38
|
+
/** Module resolver; defaults to `createRequire(import.meta.url).resolve`. */
|
|
39
|
+
resolve?: (specifier: string) => string;
|
|
40
|
+
/** Package installer; defaults to npm, then bun when npm is missing. */
|
|
41
|
+
install?: MontyNativeInstaller;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* npm package that ships Monty's `.node` addon and `monty` binary for this
|
|
46
|
+
* process. Matches `@pydantic/monty`'s `platformTriple()` / optionalDependencies.
|
|
47
|
+
*/
|
|
48
|
+
export function montyNativeBindingPackage(
|
|
49
|
+
platform: NodeJS.Platform = process.platform,
|
|
50
|
+
arch: string = process.arch,
|
|
51
|
+
): string | undefined {
|
|
52
|
+
if (platform === "darwin" && (arch === "x64" || arch === "arm64")) {
|
|
53
|
+
return `@pydantic/monty-darwin-${arch}`;
|
|
54
|
+
}
|
|
55
|
+
if (platform === "linux" && (arch === "x64" || arch === "arm64")) {
|
|
56
|
+
return `@pydantic/monty-linux-${arch}-gnu`;
|
|
57
|
+
}
|
|
58
|
+
if (platform === "win32" && arch === "x64") {
|
|
59
|
+
return "@pydantic/monty-win32-x64-msvc";
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the current platform's Monty native package, installing it in place
|
|
66
|
+
* when npm omitted the optional dependency.
|
|
67
|
+
*/
|
|
68
|
+
export async function ensureMontyNativeBinding(
|
|
69
|
+
options: EnsureMontyNativeBindingOptions = {},
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
const pkg = montyNativeBindingPackage();
|
|
72
|
+
if (!pkg) return;
|
|
73
|
+
|
|
74
|
+
const resolve = options.resolve ?? ((specifier: string) => requireFromHere.resolve(specifier));
|
|
75
|
+
if (isResolvable(pkg, resolve)) return;
|
|
76
|
+
|
|
77
|
+
const version = montyPackageVersion(resolve);
|
|
78
|
+
const spec = `${pkg}@${version}`;
|
|
79
|
+
const cwd = options.cwd ?? process.cwd();
|
|
80
|
+
logger.warn("native-binding:missing", { package: spec, cwd });
|
|
81
|
+
|
|
82
|
+
const install = options.install ?? defaultInstaller;
|
|
83
|
+
const output = await installNativePackage(spec, cwd, install);
|
|
84
|
+
if (!isResolvable(pkg, resolve)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Monty native binding ${spec} is still missing after ${output.command}. ${output.detail}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
logger.info("native-binding:installed", { package: spec, command: output.command });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isResolvable(specifier: string, resolve: (specifier: string) => string): boolean {
|
|
93
|
+
try {
|
|
94
|
+
resolve(`${specifier}/package.json`);
|
|
95
|
+
return true;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function montyPackageVersion(resolve: (specifier: string) => string): string {
|
|
102
|
+
const pkg = requireFromHere(resolve("@pydantic/monty/package.json")) as { version?: string };
|
|
103
|
+
const version = pkg.version?.trim();
|
|
104
|
+
if (!version) throw new Error("Installed @pydantic/monty has no version");
|
|
105
|
+
return version;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function installNativePackage(
|
|
109
|
+
spec: string,
|
|
110
|
+
cwd: string,
|
|
111
|
+
install: MontyNativeInstaller,
|
|
112
|
+
): Promise<{ command: string; detail: string }> {
|
|
113
|
+
const npm = await install("npm", ["install", spec, "--no-save", "--no-package-lock"], { cwd });
|
|
114
|
+
if (npm.exitCode === 0) return { command: `npm install ${spec}`, detail: npm.stdout };
|
|
115
|
+
if (npm.exitCode !== COMMAND_NOT_FOUND_EXIT_CODE) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Failed to install ${spec} with npm (exit ${npm.exitCode}): ${installDetail(npm)}`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const bun = await install("bun", ["add", spec, "--no-save"], { cwd });
|
|
122
|
+
if (bun.exitCode === 0) return { command: `bun add ${spec}`, detail: bun.stdout };
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Failed to install ${spec}: npm was not found, bun add exited ${bun.exitCode}: ${installDetail(bun)}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function defaultInstaller(
|
|
129
|
+
command: string,
|
|
130
|
+
args: readonly string[],
|
|
131
|
+
options: { cwd: string },
|
|
132
|
+
): Promise<MontyNativeInstallResult> {
|
|
133
|
+
const result = await exec.spawn(command, [...args], {
|
|
134
|
+
cwd: options.cwd,
|
|
135
|
+
stdout: "capture",
|
|
136
|
+
stderr: "capture",
|
|
137
|
+
stdin: "ignore",
|
|
138
|
+
check: false,
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
exitCode: result.exitCode,
|
|
142
|
+
stdout: result.stdout,
|
|
143
|
+
stderr: result.stderr,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function installDetail(result: MontyNativeInstallResult): string {
|
|
148
|
+
return errorUtils.errorMessage(result.stderr || result.stdout || `exit ${result.exitCode}`);
|
|
149
|
+
}
|
package/src/monty-sandbox.ts
CHANGED
|
@@ -17,10 +17,15 @@ import type {
|
|
|
17
17
|
} from "@mastra/core/workspace";
|
|
18
18
|
import type { CheckoutOptions, Monty as MontyPool, MontySession } from "@pydantic/monty/node";
|
|
19
19
|
|
|
20
|
+
import { ensureMontyNativeBinding } from "./monty-native.ts";
|
|
21
|
+
|
|
20
22
|
const DEFAULT_COMMAND_TIMEOUT_MS = 30_000;
|
|
21
23
|
const DEFAULT_MAX_MEMORY_BYTES = 10_000_000;
|
|
22
24
|
|
|
23
|
-
const montyModule = functionUtils.memoize(() =>
|
|
25
|
+
const montyModule = functionUtils.memoize(async () => {
|
|
26
|
+
await ensureMontyNativeBinding();
|
|
27
|
+
return import("@pydantic/monty/node");
|
|
28
|
+
});
|
|
24
29
|
const montyPool = functionUtils.memoize(async (): Promise<MontyPool> => {
|
|
25
30
|
const { Monty } = await montyModule();
|
|
26
31
|
return Monty.create({
|
|
@@ -140,7 +145,7 @@ export class MontySandbox implements WorkspaceSandbox {
|
|
|
140
145
|
pool,
|
|
141
146
|
{
|
|
142
147
|
limits: {
|
|
143
|
-
|
|
148
|
+
maxFeedDurationSecs: Math.max(0.001, timeout / 1_000),
|
|
144
149
|
maxMemory: this.maxMemoryBytes,
|
|
145
150
|
},
|
|
146
151
|
typeCheck: this.typeCheck,
|
package/src/remote-skills.ts
CHANGED
|
@@ -74,6 +74,7 @@ import type { OneOrMany } from "@dbx-tools/shared-core";
|
|
|
74
74
|
import type { FileSystem } from "@dbx-tools/shared-fs";
|
|
75
75
|
|
|
76
76
|
import { ASSISTANT_SHARED_SKILLS_PATH, userAssistantSkillsPath } from "./skill-paths.ts";
|
|
77
|
+
import { clearWorkspaceSkillCache } from "./skill-cache.ts";
|
|
77
78
|
|
|
78
79
|
const logger = log.logger("mastra/remote-skills");
|
|
79
80
|
|
|
@@ -441,6 +442,7 @@ export async function provisionRemoteSkills(
|
|
|
441
442
|
const localSkillPaths: string[] = [];
|
|
442
443
|
const skillNames: string[] = [];
|
|
443
444
|
let staging: LocalFileSystem | undefined;
|
|
445
|
+
let workspaceUpdated = false;
|
|
444
446
|
|
|
445
447
|
try {
|
|
446
448
|
const sources = Array.isArray(options.sources) ? options.sources : [options.sources];
|
|
@@ -452,7 +454,8 @@ export async function provisionRemoteSkills(
|
|
|
452
454
|
// the source's own stable local dir.
|
|
453
455
|
const key = cacheKey(sourceOptions);
|
|
454
456
|
const cacheFS = destination ?? localSkillsFS(key);
|
|
455
|
-
const
|
|
457
|
+
const metadata = await readMetadata(cacheFS);
|
|
458
|
+
const cached = metadata?.sources[key];
|
|
456
459
|
if (cached && isFresh(cached, resolveRefreshTtl(sourceOptions, options))) {
|
|
457
460
|
skillNames.push(...cached.skills);
|
|
458
461
|
if (!destination) localSkillPaths.push(cacheFS.root);
|
|
@@ -483,10 +486,12 @@ export async function provisionRemoteSkills(
|
|
|
483
486
|
...(policy ? { policy } : {}),
|
|
484
487
|
};
|
|
485
488
|
if (destination && databricksBasePath) {
|
|
489
|
+
await removeDeletedSkills(destination, key, metadata, record.skills);
|
|
486
490
|
await copySkillDirs(destination, staged);
|
|
487
491
|
// Only after the copy lands: a metadata entry written first would
|
|
488
492
|
// mark a failed provision as fresh and suppress the retry for a day.
|
|
489
493
|
await writeMetadata(destination, key, record);
|
|
494
|
+
workspaceUpdated = true;
|
|
490
495
|
} else {
|
|
491
496
|
localSkillPaths.push(await persistLocally(key, staged, record));
|
|
492
497
|
}
|
|
@@ -515,9 +520,35 @@ export async function provisionRemoteSkills(
|
|
|
515
520
|
}
|
|
516
521
|
}
|
|
517
522
|
|
|
523
|
+
if (workspaceUpdated && client?.config?.getHost) {
|
|
524
|
+
await clearWorkspaceSkillCache({
|
|
525
|
+
host: (await client.config.getHost()).toString(),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
518
529
|
return { localSkillPaths, databricksBasePath, skillNames };
|
|
519
530
|
}
|
|
520
531
|
|
|
532
|
+
/** Remove directories dropped by one source unless another source still owns them. */
|
|
533
|
+
async function removeDeletedSkills(
|
|
534
|
+
destination: FileSystem,
|
|
535
|
+
key: string,
|
|
536
|
+
metadata: RemoteSkillsMetadata | undefined,
|
|
537
|
+
nextSkills: readonly string[],
|
|
538
|
+
): Promise<void> {
|
|
539
|
+
const previous = metadata?.sources[key]?.skills ?? [];
|
|
540
|
+
const retained = new Set(nextSkills);
|
|
541
|
+
for (const [sourceKey, entry] of Object.entries(metadata?.sources ?? {})) {
|
|
542
|
+
if (sourceKey === key) continue;
|
|
543
|
+
for (const skill of entry.skills) retained.add(skill);
|
|
544
|
+
}
|
|
545
|
+
await Promise.all(
|
|
546
|
+
previous
|
|
547
|
+
.filter((skill) => !retained.has(skill))
|
|
548
|
+
.map((skill) => destination.rmdir(skill, { force: true, recursive: true })),
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
521
552
|
/** Probe file {@link openWritableWorkspace} writes and removes. */
|
|
522
553
|
const WRITE_PROBE_FILE = ".dbx-tools-write-probe";
|
|
523
554
|
|