@dbx-tools/appkit-mastra 0.3.44 → 0.4.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 (56) hide show
  1. package/lib/index.d.ts +71 -0
  2. package/lib/index.js +56 -0
  3. package/lib/src/agents.d.ts +347 -0
  4. package/lib/src/agents.js +554 -0
  5. package/lib/src/chart.d.ts +192 -0
  6. package/lib/src/chart.js +638 -0
  7. package/lib/src/config.d.ts +479 -0
  8. package/lib/src/config.js +190 -0
  9. package/lib/src/defaults.d.ts +68 -0
  10. package/lib/src/defaults.js +107 -0
  11. package/lib/src/filesystems.d.ts +208 -0
  12. package/lib/src/filesystems.js +958 -0
  13. package/lib/src/genie.d.ts +166 -0
  14. package/lib/src/genie.js +969 -0
  15. package/lib/src/history.d.ts +97 -0
  16. package/lib/src/history.js +264 -0
  17. package/lib/src/mcp.d.ts +66 -0
  18. package/lib/src/mcp.js +65 -0
  19. package/lib/src/memory.d.ts +111 -0
  20. package/lib/src/memory.js +275 -0
  21. package/lib/src/mlflow.d.ts +63 -0
  22. package/lib/src/mlflow.js +117 -0
  23. package/lib/src/model.d.ts +62 -0
  24. package/lib/src/model.js +168 -0
  25. package/lib/src/observability.d.ts +81 -0
  26. package/lib/src/observability.js +98 -0
  27. package/lib/src/pagination.d.ts +23 -0
  28. package/lib/src/pagination.js +31 -0
  29. package/lib/src/plugin.d.ts +352 -0
  30. package/lib/src/plugin.js +1015 -0
  31. package/lib/src/processors.d.ts +62 -0
  32. package/lib/src/processors.js +162 -0
  33. package/lib/src/rest.d.ts +36 -0
  34. package/lib/src/rest.js +46 -0
  35. package/lib/src/server.d.ts +155 -0
  36. package/lib/src/server.js +336 -0
  37. package/lib/src/serving-sanitize.d.ts +104 -0
  38. package/lib/src/serving-sanitize.js +228 -0
  39. package/lib/src/serving.d.ts +61 -0
  40. package/lib/src/serving.js +78 -0
  41. package/lib/src/statement.d.ts +51 -0
  42. package/lib/src/statement.js +83 -0
  43. package/lib/src/storage-schema.d.ts +14 -0
  44. package/lib/src/storage-schema.js +34 -0
  45. package/lib/src/summarize.d.ts +70 -0
  46. package/lib/src/summarize.js +142 -0
  47. package/lib/src/threads.d.ts +109 -0
  48. package/lib/src/threads.js +301 -0
  49. package/lib/src/validation.d.ts +19 -0
  50. package/lib/src/validation.js +17 -0
  51. package/lib/src/workspaces.d.ts +68 -0
  52. package/lib/src/workspaces.js +246 -0
  53. package/lib/src/writer.d.ts +25 -0
  54. package/lib/src/writer.js +40 -0
  55. package/lib/tsconfig.tsbuildinfo +1 -0
  56. package/package.json +17 -13
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Lakebase-backed Mastra memory wiring.
3
+ *
4
+ * Provides a {@link MemoryBuilder} that mints one `Memory` per agent
5
+ * with two independent knobs:
6
+ *
7
+ * - **Storage** (threads / messages via `PostgresStore`): defaults to
8
+ * **per-agent** namespacing via {@link agentStorageSchemaName} so
9
+ * conversation history stays isolated between agents in the same
10
+ * database. `PostgresStore` auto-creates the schema with
11
+ * `CREATE SCHEMA IF NOT EXISTS` on init.
12
+ * - **Memory** (semantic recall via `PgVector`): defaults to a single
13
+ * **shared** instance across every agent. Cross-agent recall on one
14
+ * index is almost always what users want; opt into per-agent recall
15
+ * by passing a {@link MastraMemoryConfigOverride} on the agent.
16
+ *
17
+ * Additionally, {@link MemoryBuilder.instanceStorage} returns a
18
+ * **Mastra-instance-level** `PostgresStore` (schema `mastra_instance`)
19
+ * used for workflow snapshots - the persistence layer
20
+ * `agent.resumeStream()` reads from when waking a suspended
21
+ * `requireApproval` tool call. Per-agent stores are not enough for
22
+ * this: workflow runs are scoped to the Mastra instance, not an
23
+ * individual agent's `Memory`.
24
+ *
25
+ * Plugin-level `config.storage` / `config.memory` act as the baseline
26
+ * (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
27
+ * is registered); per-agent settings cascade on top of that.
28
+ *
29
+ * @module
30
+ */
31
+ import { getUsernameWithApiLookup } from "@databricks/appkit";
32
+ import { hash, log } from "@dbx-tools/shared-core";
33
+ import { fastembed } from "@mastra/fastembed";
34
+ import { Memory } from "@mastra/memory";
35
+ import { PgVector, PostgresStore } from "@mastra/pg";
36
+ import { Pool } from "pg";
37
+ import { agentStorageSchemaName } from "./storage-schema.js";
38
+ import { summaryModel, TITLE_INSTRUCTIONS } from "./summarize.js";
39
+ const logger = log.logger("mastra/memory");
40
+ /**
41
+ * Build a dedicated **service-principal** Lakebase pool for Mastra
42
+ * memory from the lakebase plugin's resolved SP pg config.
43
+ *
44
+ * The plugin's `exports().pool` is a `RoutingPool` that switches to
45
+ * the per-user (OBO) pool whenever a query runs inside an `asUser`
46
+ * scope - exactly the context the mastra plugin establishes around
47
+ * every chat turn. Memory (threads / messages + semantic recall) must
48
+ * instead always act as the app service principal: it owns the
49
+ * auto-created `mastra_*` schemas (a per-user role usually can't
50
+ * `CREATE SCHEMA`) and is shared across users, so it cannot inherit a
51
+ * request's OBO identity.
52
+ *
53
+ * `pgConfig` must be the plugin's `exports().getPgConfig()` evaluated
54
+ * **outside** any `asUser` scope (i.e. during setup), so it carries
55
+ * the SP connection target, OAuth token-refresh `password` callback,
56
+ * and any `lakebase({ pool })` tuning overrides - all of which this
57
+ * pool inherits. See the call site in `plugin.ts`.
58
+ */
59
+ export async function createServicePrincipalPool(pgConfig) {
60
+ // `getPgConfig()` resolves the SP username synchronously from
61
+ // `PGUSER` / `DATABRICKS_CLIENT_ID`; fall back to the async API
62
+ // lookup (e.g. local dev authenticating via PAT) so the pool always
63
+ // has an identity to connect with.
64
+ const user = pgConfig.user ?? (await getUsernameWithApiLookup());
65
+ return new Pool({ ...pgConfig, user });
66
+ }
67
+ /**
68
+ * True when any plugin-level or per-agent setting could need the
69
+ * Lakebase pool. Used by `plugin.ts` to gate creation of the
70
+ * service-principal pool and the {@link MemoryBuilder} that consumes
71
+ * it; when false neither is built.
72
+ */
73
+ export function needsLakebase(config) {
74
+ if (settingNeedsSharedPool(config.storage))
75
+ return true;
76
+ if (settingNeedsSharedPool(config.memory))
77
+ return true;
78
+ const defs = collectAgentDefinitions(config);
79
+ return defs.some((d) => settingNeedsSharedPool(d.storage) || settingNeedsSharedPool(d.memory));
80
+ }
81
+ /**
82
+ * Construct a per-agent {@link Memory} factory bound to the supplied
83
+ * service-principal pool (see {@link createServicePrincipalPool}).
84
+ * Caches the shared `PgVector` singleton (built on first need) so each
85
+ * agent build is O(1) after the first.
86
+ */
87
+ export function createMemoryBuilder(config, servicePrincipalPool) {
88
+ return new MemoryBuilder(config, servicePrincipalPool);
89
+ }
90
+ /**
91
+ * Builds one `Memory` per agent against a shared service-principal
92
+ * Lakebase pool. Per-instance state keeps the shared `PgVector` alive
93
+ * across calls so registering N agents stays cheap.
94
+ */
95
+ export class MemoryBuilder {
96
+ config;
97
+ servicePrincipalPool;
98
+ sharedVector;
99
+ constructor(config, servicePrincipalPool) {
100
+ this.config = config;
101
+ this.servicePrincipalPool = servicePrincipalPool;
102
+ }
103
+ /**
104
+ * Build the Mastra-instance-level storage used for workflow
105
+ * snapshots. Returns `undefined` when plugin-level `storage` is
106
+ * disabled, in which case `agent.resumeStream()` (and therefore
107
+ * the `requireApproval` flow) will not be available.
108
+ *
109
+ * The store lives in a dedicated `mastra_instance` schema so it
110
+ * never collides with per-agent {@link agentStorageSchemaName} namespaces.
111
+ * Workflow snapshots are not per-agent state; they belong to the
112
+ * `Mastra` instance that owns the workflow execution.
113
+ */
114
+ instanceStorage() {
115
+ const setting = this.config.storage;
116
+ if (!setting)
117
+ return undefined;
118
+ if (typeof setting === "object") {
119
+ return new PostgresStore(withId(setting, "mastra-store__instance"));
120
+ }
121
+ return new PostgresStore({
122
+ id: "mastra-store__instance",
123
+ schemaName: "mastra_instance",
124
+ pool: this.servicePrincipalPool,
125
+ });
126
+ }
127
+ /**
128
+ * Build a `Memory` for `agentId` after the plugin/agent cascade.
129
+ * Returns `undefined` when the agent has neither storage nor a
130
+ * vector store enabled - Mastra accepts a missing `memory` field
131
+ * and treats the agent as stateless.
132
+ */
133
+ forAgent(agentId, def) {
134
+ const storageSetting = def.storage ?? this.config.storage;
135
+ const memorySetting = def.memory ?? this.config.memory;
136
+ const storage = this.buildStorage(agentId, storageSetting);
137
+ const vector = this.buildVector(memorySetting);
138
+ if (!storage && !vector) {
139
+ logger.debug("agent:stateless", { agentId });
140
+ return undefined;
141
+ }
142
+ logger.debug("agent:configured", {
143
+ agentId,
144
+ storage: storage !== undefined,
145
+ vector: vector !== undefined,
146
+ vectorMode: vector === undefined ? "off" : typeof memorySetting === "object" ? "dedicated" : "shared",
147
+ });
148
+ return new Memory({
149
+ ...(storage ? { storage } : {}),
150
+ ...(vector ? { vector, embedder: fastembed } : {}),
151
+ options: {
152
+ lastMessages: 10,
153
+ ...(vector ? { semanticRecall: { topK: 3, messageRange: 2 } } : {}),
154
+ // Auto-name each thread from its opening turn so the
155
+ // conversation list the UI renders shows meaningful titles
156
+ // instead of raw ids. Titling runs on the small / fast chat
157
+ // tier (see `summarize.ts`) rather than the agent's primary
158
+ // model, so naming a thread never spends the heavyweight model.
159
+ // Only meaningful when storage is on; harmless otherwise.
160
+ ...(storage
161
+ ? {
162
+ generateTitle: {
163
+ model: summaryModel(this.config),
164
+ instructions: TITLE_INSTRUCTIONS,
165
+ },
166
+ }
167
+ : {}),
168
+ },
169
+ });
170
+ }
171
+ buildStorage(agentId, setting) {
172
+ if (!setting)
173
+ return undefined;
174
+ if (typeof setting === "boolean") {
175
+ return new PostgresStore({
176
+ id: `mastra-store__${agentId}`,
177
+ schemaName: agentStorageSchemaName(agentId),
178
+ pool: this.servicePrincipalPool,
179
+ });
180
+ }
181
+ // Cast: `withId` guarantees `id` is set, but the distributive
182
+ // Omit + `id?: string` shape doesn't structurally narrow to the
183
+ // discriminated union members. Runtime shape is identical.
184
+ return new PostgresStore(withId(setting, `mastra-store__${agentId}`));
185
+ }
186
+ /**
187
+ * Resolve the agent's vector store. Cascade:
188
+ *
189
+ * - falsy: no vector.
190
+ * - `boolean` / `undefined-inheriting-true`: return the shared
191
+ * singleton (built lazily on first call). All agents that
192
+ * default-enable memory write into and recall from one index.
193
+ * - object: build a dedicated `PgVector` for this agent.
194
+ */
195
+ buildVector(setting) {
196
+ if (!setting)
197
+ return undefined;
198
+ if (typeof setting === "boolean")
199
+ return this.getSharedVector();
200
+ return buildPgVector(setting);
201
+ }
202
+ getSharedVector() {
203
+ if (!this.sharedVector) {
204
+ this.sharedVector = buildSharedPgVector(this.servicePrincipalPool);
205
+ }
206
+ return this.sharedVector;
207
+ }
208
+ }
209
+ /**
210
+ * Build the shared `PgVector` that backs the default
211
+ * `def.memory === true` case across every agent.
212
+ *
213
+ * `PgVector`'s constructor accepts only connection-style configs
214
+ * (`HostConfig` / `ConnectionStringConfig` / `ClientConfig`); there is
215
+ * no `{ pool }` shorthand the way `PostgresStore` has one. Worse, the
216
+ * constructor synchronously kicks off a `cacheWarmupPromise` IIFE that
217
+ * calls `this.pool.connect()` before returning, so we can't cleanly
218
+ * hand it an inert config and patch the pool afterwards.
219
+ *
220
+ * The trick: pass illegal-but-validation-passing placeholders so the
221
+ * warmup's `net.connect()` rejects synchronously with `RangeError`
222
+ * (Node validates `0 <= port < 65536`). The IIFE's `catch {}` swallows
223
+ * it, no DNS lookup or TCP attempt happens, and we then swap
224
+ * `pgVector.pool` to the lakebase pool. Every subsequent `PgVector`
225
+ * method reads `this.pool` at call time, so all real I/O goes through
226
+ * the lakebase pool from then on. The placeholder pool is `.end()`'d
227
+ * so its socket book-keeping is released.
228
+ */
229
+ function buildSharedPgVector(pool) {
230
+ const vector = new PgVector({
231
+ id: `pg${hash.id()}`,
232
+ // Keep the recall index out of `public`: on a Lakebase database the app
233
+ // service principal has no CREATE on `public` (PG15+ locks it down), so a
234
+ // default-schema PgVector fails on CREATE INDEX with "permission denied for
235
+ // schema public". PgVector runs CREATE SCHEMA IF NOT EXISTS for a named
236
+ // schema, so the SP creates and owns `mastra_instance` (the same
237
+ // instance-level schema the workflow-snapshot store uses) and the index
238
+ // lands there. Table names don't collide with the storage tables.
239
+ schemaName: "mastra_instance",
240
+ host: "-1",
241
+ port: -1,
242
+ database: "_",
243
+ user: "_",
244
+ password: "_",
245
+ });
246
+ const placeholder = vector.pool;
247
+ vector.pool = pool;
248
+ void placeholder.end().catch(() => undefined);
249
+ return vector;
250
+ }
251
+ /** Per-agent dedicated `PgVector` (rare; opt-in via object override). */
252
+ function buildPgVector(setting) {
253
+ return new PgVector(withId(setting, `pg-vector__${hash.id()}`));
254
+ }
255
+ /** True when this setting requires the shared Lakebase pool. */
256
+ function settingNeedsSharedPool(setting) {
257
+ return setting === true;
258
+ }
259
+ /** Walk the three shapes of `config.agents` into a flat list. */
260
+ function collectAgentDefinitions(config) {
261
+ const agents = config.agents;
262
+ if (!agents)
263
+ return [];
264
+ if (Array.isArray(agents))
265
+ return agents;
266
+ if (typeof agents.instructions === "string") {
267
+ return [agents];
268
+ }
269
+ return Object.values(agents);
270
+ }
271
+ /** Fill in a default `id` when the caller didn't supply one. */
272
+ function withId(value, fallback) {
273
+ return value.id ? value : { ...value, id: fallback };
274
+ }
275
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVtb3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21lbW9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E2Qkc7QUFFSCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5RCxPQUFPLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUM5QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDckQsT0FBTyxFQUFFLElBQUksRUFBbUIsTUFBTSxJQUFJLENBQUM7QUFJM0MsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDMUQsT0FBTyxFQUFFLFlBQVksRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUUvRCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBRTNDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLDBCQUEwQixDQUFDLFFBQW9CO0lBQ25FLDhEQUE4RDtJQUM5RCxnRUFBZ0U7SUFDaEUsb0VBQW9FO0lBQ3BFLG1DQUFtQztJQUNuQyxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSx3QkFBd0IsRUFBRSxDQUFDLENBQUM7SUFDakUsT0FBTyxJQUFJLElBQUksQ0FBQyxFQUFFLEdBQUcsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFDekMsQ0FBQztBQU1EOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGFBQWEsQ0FBQyxNQUEwQjtJQUN0RCxJ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QUM5QyxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQseUVBQXlFO0FBQ3pFLFNBQVMsYUFBYSxDQUFDLE9BQW1DO0lBQ3hELE9BQU8sSUFBSSxRQUFRLENBQ2pCLE1BQU0sQ0FBQyxPQUFPLEVBQUUsY0FBYyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBOEMsQ0FDeEYsQ0FBQztBQUNKLENBQUM7QUFFRCxnRUFBZ0U7QUFDaEUsU0FBUyxzQkFBc0IsQ0FBQyxPQUFtRDtJQUNqRixPQUFPLE9BQU8sS0FBSyxJQUFJLENBQUM7QUFDMUIsQ0FBQztBQUVELGlFQUFpRTtBQUNqRSxTQUFTLHVCQUF1QixDQUFDLE1BQTBCO0lBQ3pELE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDN0IsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUN2QixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBQUUsT0FBTyxNQUFNLENBQUM7SUFDekMsSUFBSSxPQUFRLE1BQWdDLENBQUMsWUFBWSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ3ZFLE9BQU8sQ0FBQyxNQUErQixDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUErQyxDQUFDLENBQUM7QUFDeEUsQ0FBQztBQUVELGdFQUFnRTtBQUNoRSxTQUFTLE1BQU0sQ0FBNEIsS0FBUSxFQUFFLFFBQWdCO0lBQ25FLE9BQU8sS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUN2RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBMYWtlYmFzZS1iYWNrZWQgTWFzdHJhIG1lbW9yeSB3aXJpbmcuXG4gKlxuICogUHJvdmlkZXMgYSB7QGxpbmsgTWVtb3J5QnVpbGRlcn0gdGhhdCBtaW50cyBvbmUgYE1lbW9yeWAgcGVyIGFnZW50XG4gKiB3aXRoIHR3byBpbmRlcGVuZGVudCBrbm9iczpcbiAqXG4gKiAtICoqU3RvcmFnZSoqICh0aHJlYWRzIC8gbWVzc2FnZXMgdmlhIGBQb3N0Z3Jlc1N0b3JlYCk6IGRlZmF1bHRzIHRvXG4gKiAgICoqcGVyLWFnZW50KiogbmFtZXNwYWNpbmcgdmlhIHtAbGluayBhZ2VudFN0b3JhZ2VTY2hlbWFOYW1lfSBzb1xuICogICBjb252ZXJzYXRpb24gaGlzdG9yeSBzdGF5cyBpc29sYXRlZCBiZXR3ZWVuIGFnZW50cyBpbiB0aGUgc2FtZVxuICogICBkYXRhYmFzZS4gYFBvc3RncmVzU3RvcmVgIGF1dG8tY3JlYXRlcyB0aGUgc2NoZW1hIHdpdGhcbiAqICAgYENSRUFURSBTQ0hFTUEgSUYgTk9UIEVYSVNUU2Agb24gaW5pdC5cbiAqIC0gKipNZW1vcnkqKiAoc2VtYW50aWMgcmVjYWxsIHZpYSBgUGdWZWN0b3JgKTogZGVmYXVsdHMgdG8gYSBzaW5nbGVcbiAqICAgKipzaGFyZWQqKiBpbnN0YW5jZSBhY3Jvc3MgZXZlcnkgYWdlbnQuIENyb3NzLWFnZW50IHJlY2FsbCBvbiBvbmVcbiAqICAgaW5kZXggaXMgYWxtb3N0IGFsd2F5cyB3aGF0IHVzZXJzIHdhbnQ7IG9wdCBpbnRvIHBlci1hZ2VudCByZWNhbGxcbiAqICAgYnkgcGFzc2luZyBhIHtAbGluayBNYXN0cmFNZW1vcnlDb25maWdPdmVycmlkZX0gb24gdGhlIGFnZW50LlxuICpcbiAqIEFkZGl0aW9uYWxseSwge0BsaW5rIE1lbW9yeUJ1aWxkZXIuaW5zdGFuY2VTdG9yYWdlfSByZXR1cm5zIGFcbiAqICoqTWFzdHJhLWluc3RhbmNlLWxldmVsKiogYFBvc3RncmVzU3RvcmVgIChzY2hlbWEgYG1hc3RyYV9pbnN0YW5jZWApXG4gKiB1c2VkIGZvciB3b3JrZmxvdyBzbmFwc2hvdHMgLSB0aGUgcGVyc2lzdGVuY2UgbGF5ZXJcbiAqIGBhZ2VudC5yZXN1bWVTdHJlYW0oKWAgcmVhZHMgZnJvbSB3aGVuIHdha2luZyBhIHN1c3BlbmRlZFxuICogYHJlcXVpcmVBcHByb3ZhbGAgdG9vbCBjYWxsLiBQZXItYWdlbnQgc3RvcmVzIGFyZSBub3QgZW5vdWdoIGZvclxuICogdGhpczogd29ya2Zsb3cgcnVucyBhcmUgc2NvcGVkIHRvIHRoZSBNYXN0cmEgaW5zdGFuY2UsIG5vdCBhblxuICogaW5kaXZpZHVhbCBhZ2VudCdzIGBNZW1vcnlgLlxuICpcbiAqIFBsdWdpbi1sZXZlbCBgY29uZmlnLnN0b3JhZ2VgIC8gYGNvbmZpZy5tZW1vcnlgIGFjdCBhcyB0aGUgYmFzZWxpbmVcbiAqIChhdXRvLWRlZmF1bHRlZCB0byBgdHJ1ZWAgaW4gYHBsdWdpbi50c2Agd2hlbiB0aGUgYGxha2ViYXNlYCBwbHVnaW5cbiAqIGlzIHJlZ2lzdGVyZWQpOyBwZXItYWdlbnQgc2V0dGluZ3MgY2FzY2FkZSBvbiB0b3Agb2YgdGhhdC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgZ2V0VXNlcm5hbWVXaXRoQXBpTG9va3VwIH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHsgaGFzaCwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IGZhc3RlbWJlZCB9IGZyb20gXCJAbWFzdHJhL2Zhc3RlbWJlZFwiO1xuaW1wb3J0IHsgTWVtb3J5IH0gZnJvbSBcIkBtYXN0cmEvbWVtb3J5XCI7XG5pbXBvcnQgeyBQZ1ZlY3RvciwgUG9zdGdyZXNTdG9yZSB9IGZyb20gXCJAbWFzdHJhL3BnXCI7XG5pbXBvcnQgeyBQb29sLCB0eXBlIFBvb2xDb25maWcgfSBmcm9tIFwicGdcIjtcblxuaW1wb3J0IHR5cGUgeyBNYXN0cmFBZ2VudERlZmluaXRpb24sIE1hc3RyYU1lbW9yeUNvbmZpZ092ZXJyaWRlIH0gZnJvbSBcIi4vYWdlbnRzXCI7XG5pbXBvcnQgdHlwZSB7IE1hc3RyYVBsdWdpbkNvbmZpZyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuaW1wb3J0IHsgYWdlbnRTdG9yYWdlU2NoZW1hTmFtZSB9IGZyb20gXCIuL3N0b3JhZ2Utc2NoZW1hXCI7XG5pbXBvcnQgeyBzdW1tYXJ5TW9kZWwsIFRJVExFX0lOU1RSVUNUSU9OUyB9IGZyb20gXCIuL3N1bW1hcml6ZVwiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwibWFzdHJhL21lbW9yeVwiKTtcblxuLyoqXG4gKiBCdWlsZCBhIGRlZGljYXRlZCAqKnNlcnZpY2UtcHJpbmNpcGFsKiogTGFrZWJhc2UgcG9vbCBmb3IgTWFzdHJhXG4gKiBtZW1vcnkgZnJvbSB0aGUgbGFrZWJhc2UgcGx1Z2luJ3MgcmVzb2x2ZWQgU1AgcGcgY29uZmlnLlxuICpcbiAqIFRoZSBwbHVnaW4ncyBgZXhwb3J0cygpLnBvb2xgIGlzIGEgYFJvdXRpbmdQb29sYCB0aGF0IHN3aXRjaGVzIHRvXG4gKiB0aGUgcGVyLXVzZXIgKE9CTykgcG9vbCB3aGVuZXZlciBhIHF1ZXJ5IHJ1bnMgaW5zaWRlIGFuIGBhc1VzZXJgXG4gKiBzY29wZSAtIGV4YWN0bHkgdGhlIGNvbnRleHQgdGhlIG1hc3RyYSBwbHVnaW4gZXN0YWJsaXNoZXMgYXJvdW5kXG4gKiBldmVyeSBjaGF0IHR1cm4uIE1lbW9yeSAodGhyZWFkcyAvIG1lc3NhZ2VzICsgc2VtYW50aWMgcmVjYWxsKSBtdXN0XG4gKiBpbnN0ZWFkIGFsd2F5cyBhY3QgYXMgdGhlIGFwcCBzZXJ2aWNlIHByaW5jaXBhbDogaXQgb3ducyB0aGVcbiAqIGF1dG8tY3JlYXRlZCBgbWFzdHJhXypgIHNjaGVtYXMgKGEgcGVyLXVzZXIgcm9sZSB1c3VhbGx5IGNhbid0XG4gKiBgQ1JFQVRFIFNDSEVNQWApIGFuZCBpcyBzaGFyZWQgYWNyb3NzIHVzZXJzLCBzbyBpdCBjYW5ub3QgaW5oZXJpdCBhXG4gKiByZXF1ZXN0J3MgT0JPIGlkZW50aXR5LlxuICpcbiAqIGBwZ0NvbmZpZ2AgbXVzdCBiZSB0aGUgcGx1Z2luJ3MgYGV4cG9ydHMoKS5nZXRQZ0NvbmZpZygpYCBldmFsdWF0ZWRcbiAqICoqb3V0c2lkZSoqIGFueSBgYXNVc2VyYCBzY29wZSAoaS5lLiBkdXJpbmcgc2V0dXApLCBzbyBpdCBjYXJyaWVzXG4gKiB0aGUgU1AgY29ubmVjdGlvbiB0YXJnZXQsIE9BdXRoIHRva2VuLXJlZnJlc2ggYHBhc3N3b3JkYCBjYWxsYmFjayxcbiAqIGFuZCBhbnkgYGxha2ViYXNlKHsgcG9vbCB9KWAgdHVuaW5nIG92ZXJyaWRlcyAtIGFsbCBvZiB3aGljaCB0aGlzXG4gKiBwb29sIGluaGVyaXRzLiBTZWUgdGhlIGNhbGwgc2l0ZSBpbiBgcGx1Z2luLnRzYC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVNlcnZpY2VQcmluY2lwYWxQb29sKHBnQ29uZmlnOiBQb29sQ29uZmlnKTogUHJvbWlzZTxQb29sPiB7XG4gIC8vIGBnZXRQZ0NvbmZpZygpYCByZXNvbHZlcyB0aGUgU1AgdXNlcm5hbWUgc3luY2hyb25vdXNseSBmcm9tXG4gIC8vIGBQR1VTRVJgIC8gYERBVEFCUklDS1NfQ0xJRU5UX0lEYDsgZmFsbCBiYWNrIHRvIHRoZSBhc3luYyBBUElcbiAgLy8gbG9va3VwIChlLmcuIGxvY2FsIGRldiBhdXRoZW50aWNhdGluZyB2aWEgUEFUKSBzbyB0aGUgcG9vbCBhbHdheXNcbiAgLy8gaGFzIGFuIGlkZW50aXR5IHRvIGNvbm5lY3Qgd2l0aC5cbiAgY29uc3QgdXNlciA9IHBnQ29uZmlnLnVzZXIgPz8gKGF3YWl0IGdldFVzZXJuYW1lV2l0aEFwaUxvb2t1cCgpKTtcbiAgcmV0dXJuIG5ldyBQb29sKHsgLi4ucGdDb25maWcsIHVzZXIgfSk7XG59XG5cbi8qKiBFZmZlY3RpdmUgcGVyLWtub2Igc2V0dGluZyBhZnRlciB0aGUgcGx1Z2luL2FnZW50IGNhc2NhZGUuICovXG50eXBlIFN0b3JhZ2VTZXR0aW5nID0gTWFzdHJhQWdlbnREZWZpbml0aW9uW1wic3RvcmFnZVwiXTtcbnR5cGUgTWVtb3J5U2V0dGluZyA9IE1hc3RyYUFnZW50RGVmaW5pdGlvbltcIm1lbW9yeVwiXTtcblxuLyoqXG4gKiBUcnVlIHdoZW4gYW55IHBsdWdpbi1sZXZlbCBvciBwZXItYWdlbnQgc2V0dGluZyBjb3VsZCBuZWVkIHRoZVxuICogTGFrZWJhc2UgcG9vbC4gVXNlZCBieSBgcGx1Z2luLnRzYCB0byBnYXRlIGNyZWF0aW9uIG9mIHRoZVxuICogc2VydmljZS1wcmluY2lwYWwgcG9vbCBhbmQgdGhlIHtAbGluayBNZW1vcnlCdWlsZGVyfSB0aGF0IGNvbnN1bWVzXG4gKiBpdDsgd2hlbiBmYWxzZSBuZWl0aGVyIGlzIGJ1aWx0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gbmVlZHNMYWtlYmFzZShjb25maWc6IE1hc3RyYVBsdWdpbkNvbmZpZyk6IGJvb2xlYW4ge1xuICBpZiAoc2V0dGluZ05lZWRzU2hhcmVkUG9vbChjb25maWcuc3RvcmFnZSkpIHJldHVybiB0cnVlO1xuICBpZiAoc2V0dGluZ05lZWRzU2hhcmVkUG9vbChjb25maWcubWVtb3J5KSkgcmV0dXJuIHRydWU7XG4gIGNvbnN0IGRlZnMgPSBjb2xsZWN0QWdlbnREZWZpbml0aW9ucyhjb25maWcpO1xuICByZXR1cm4gZGVmcy5zb21lKChkKSA9PiBzZXR0aW5nTmVlZHNTaGFyZWRQb29sKGQuc3RvcmFnZSkgfHwgc2V0dGluZ05lZWRzU2hhcmVkUG9vbChkLm1lbW9yeSkpO1xufVxuXG4vKipcbiAqIENvbnN0cnVjdCBhIHBlci1hZ2VudCB7QGxpbmsgTWVtb3J5fSBmYWN0b3J5IGJvdW5kIHRvIHRoZSBzdXBwbGllZFxuICogc2VydmljZS1wcmluY2lwYWwgcG9vbCAoc2VlIHtAbGluayBjcmVhdGVTZXJ2aWNlUHJpbmNpcGFsUG9vbH0pLlxuICogQ2FjaGVzIHRoZSBzaGFyZWQgYFBnVmVjdG9yYCBzaW5nbGV0b24gKGJ1aWx0IG9uIGZpcnN0IG5lZWQpIHNvIGVhY2hcbiAqIGFnZW50IGJ1aWxkIGlzIE8oMSkgYWZ0ZXIgdGhlIGZpcnN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTWVtb3J5QnVpbGRlcihcbiAgY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcsXG4gIHNlcnZpY2VQcmluY2lwYWxQb29sOiBQb29sLFxuKTogTWVtb3J5QnVpbGRlciB7XG4gIHJldHVybiBuZXcgTWVtb3J5QnVpbGRlcihjb25maWcsIHNlcnZpY2VQcmluY2lwYWxQb29sKTtcbn1cblxuLyoqXG4gKiBCdWlsZHMgb25lIGBNZW1vcnlgIHBlciBhZ2VudCBhZ2FpbnN0IGEgc2hhcmVkIHNlcnZpY2UtcHJpbmNpcGFsXG4gKiBMYWtlYmFzZSBwb29sLiBQZXItaW5zdGFuY2Ugc3RhdGUga2VlcHMgdGhlIHNoYXJlZCBgUGdWZWN0b3JgIGFsaXZlXG4gKiBhY3Jvc3MgY2FsbHMgc28gcmVnaXN0ZXJpbmcgTiBhZ2VudHMgc3RheXMgY2hlYXAuXG4gKi9cbmV4cG9ydCBjbGFzcyBNZW1vcnlCdWlsZGVyIHtcbiAgcHJpdmF0ZSBzaGFyZWRWZWN0b3I6IFBnVmVjdG9yIHwgdW5kZWZpbmVkO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcsXG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXJ2aWNlUHJpbmNpcGFsUG9vbDogUG9vbCxcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBCdWlsZCB0aGUgTWFzdHJhLWluc3RhbmNlLWxldmVsIHN0b3JhZ2UgdXNlZCBmb3Igd29ya2Zsb3dcbiAgICogc25hcHNob3RzLiBSZXR1cm5zIGB1bmRlZmluZWRgIHdoZW4gcGx1Z2luLWxldmVsIGBzdG9yYWdlYCBpc1xuICAgKiBkaXNhYmxlZCwgaW4gd2hpY2ggY2FzZSBgYWdlbnQucmVzdW1lU3RyZWFtKClgIChhbmQgdGhlcmVmb3JlXG4gICAqIHRoZSBgcmVxdWlyZUFwcHJvdmFsYCBmbG93KSB3aWxsIG5vdCBiZSBhdmFpbGFibGUuXG4gICAqXG4gICAqIFRoZSBzdG9yZSBsaXZlcyBpbiBhIGRlZGljYXRlZCBgbWFzdHJhX2luc3RhbmNlYCBzY2hlbWEgc28gaXRcbiAgICogbmV2ZXIgY29sbGlkZXMgd2l0aCBwZXItYWdlbnQge0BsaW5rIGFnZW50U3RvcmFnZVNjaGVtYU5hbWV9IG5hbWVzcGFjZXMuXG4gICAqIFdvcmtmbG93IHNuYXBzaG90cyBhcmUgbm90IHBlci1hZ2VudCBzdGF0ZTsgdGhleSBiZWxvbmcgdG8gdGhlXG4gICAqIGBNYXN0cmFgIGluc3RhbmNlIHRoYXQgb3ducyB0aGUgd29ya2Zsb3cgZXhlY3V0aW9uLlxuICAgKi9cbiAgaW5zdGFuY2VTdG9yYWdlKCk6IFBvc3RncmVzU3RvcmUgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IHNldHRpbmcgPSB0aGlzLmNvbmZpZy5zdG9yYWdlO1xuICAgIGlmICghc2V0dGluZykgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBpZiAodHlwZW9mIHNldHRpbmcgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIHJldHVybiBuZXcgUG9zdGdyZXNTdG9yZShcbiAgICAgICAgd2l0aElkKHNldHRpbmcsIFwibWFzdHJhLXN0b3JlX19pbnN0YW5jZVwiKSBhcyBDb25zdHJ1Y3RvclBhcmFtZXRlcnM8dHlwZW9mIFBvc3RncmVzU3RvcmU+WzBdLFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBQb3N0Z3Jlc1N0b3JlKHtcbiAgICAgIGlkOiBcIm1hc3RyYS1zdG9yZV9faW5zdGFuY2VcIixcbiAgICAgIHNjaGVtYU5hbWU6IFwibWFzdHJhX2luc3RhbmNlXCIsXG4gICAgICBwb29sOiB0aGlzLnNlcnZpY2VQcmluY2lwYWxQb29sLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkIGEgYE1lbW9yeWAgZm9yIGBhZ2VudElkYCBhZnRlciB0aGUgcGx1Z2luL2FnZW50IGNhc2NhZGUuXG4gICAqIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiB0aGUgYWdlbnQgaGFzIG5laXRoZXIgc3RvcmFnZSBub3IgYVxuICAgKiB2ZWN0b3Igc3RvcmUgZW5hYmxlZCAtIE1hc3RyYSBhY2NlcHRzIGEgbWlzc2luZyBgbWVtb3J5YCBmaWVsZFxuICAgKiBhbmQgdHJlYXRzIHRoZSBhZ2VudCBhcyBzdGF0ZWxlc3MuXG4gICAqL1xuICBmb3JBZ2VudChhZ2VudElkOiBzdHJpbmcsIGRlZjogTWFzdHJhQWdlbnREZWZpbml0aW9uKTogTWVtb3J5IHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBzdG9yYWdlU2V0dGluZyA9IGRlZi5zdG9yYWdlID8/IHRoaXMuY29uZmlnLnN0b3JhZ2U7XG4gICAgY29uc3QgbWVtb3J5U2V0dGluZyA9IGRlZi5tZW1vcnkgPz8gdGhpcy5jb25maWcubWVtb3J5O1xuXG4gICAgY29uc3Qgc3RvcmFnZSA9IHRoaXMuYnVpbGRTdG9yYWdlKGFnZW50SWQsIHN0b3JhZ2VTZXR0aW5nKTtcbiAgICBjb25zdCB2ZWN0b3IgPSB0aGlzLmJ1aWxkVmVjdG9yKG1lbW9yeVNldHRpbmcpO1xuICAgIGlmICghc3RvcmFnZSAmJiAhdmVjdG9yKSB7XG4gICAgICBsb2dnZXIuZGVidWcoXCJhZ2VudDpzdGF0ZWxlc3NcIiwgeyBhZ2VudElkIH0pO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBsb2dnZXIuZGVidWcoXCJhZ2VudDpjb25maWd1cmVkXCIsIHtcbiAgICAgIGFnZW50SWQsXG4gICAgICBzdG9yYWdlOiBzdG9yYWdlICE9PSB1bmRlZmluZWQsXG4gICAgICB2ZWN0b3I6IHZlY3RvciAhPT0gdW5kZWZpbmVkLFxuICAgICAgdmVjdG9yTW9kZTpcbiAgICAgICAgdmVjdG9yID09PSB1bmRlZmluZWQgPyBcIm9mZlwiIDogdHlwZW9mIG1lbW9yeVNldHRpbmcgPT09IFwib2JqZWN0XCIgPyBcImRlZGljYXRlZFwiIDogXCJzaGFyZWRcIixcbiAgICB9KTtcblxuICAgIHJldHVybiBuZXcgTWVtb3J5KHtcbiAgICAgIC4uLihzdG9yYWdlID8geyBzdG9yYWdlIH0gOiB7fSksXG4gICAgICAuLi4odmVjdG9yID8geyB2ZWN0b3IsIGVtYmVkZGVyOiBmYXN0ZW1iZWQgfSA6IHt9KSxcbiAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgbGFzdE1lc3NhZ2VzOiAxMCxcbiAgICAgICAgLi4uKHZlY3RvciA/IHsgc2VtYW50aWNSZWNhbGw6IHsgdG9wSzogMywgbWVzc2FnZVJhbmdlOiAyIH0gfSA6IHt9KSxcbiAgICAgICAgLy8gQXV0by1uYW1lIGVhY2ggdGhyZWFkIGZyb20gaXRzIG9wZW5pbmcgdHVybiBzbyB0aGVcbiAgICAgICAgLy8gY29udmVyc2F0aW9uIGxpc3QgdGhlIFVJIHJlbmRlcnMgc2hvd3MgbWVhbmluZ2Z1bCB0aXRsZXNcbiAgICAgICAgLy8gaW5zdGVhZCBvZiByYXcgaWRzLiBUaXRsaW5nIHJ1bnMgb24gdGhlIHNtYWxsIC8gZmFzdCBjaGF0XG4gICAgICAgIC8vIHRpZXIgKHNlZSBgc3VtbWFyaXplLnRzYCkgcmF0aGVyIHRoYW4gdGhlIGFnZW50J3MgcHJpbWFyeVxuICAgICAgICAvLyBtb2RlbCwgc28gbmFtaW5nIGEgdGhyZWFkIG5ldmVyIHNwZW5kcyB0aGUgaGVhdnl3ZWlnaHQgbW9kZWwuXG4gICAgICAgIC8vIE9ubHkgbWVhbmluZ2Z1bCB3aGVuIHN0b3JhZ2UgaXMgb247IGhhcm1sZXNzIG90aGVyd2lzZS5cbiAgICAgICAgLi4uKHN0b3JhZ2VcbiAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgZ2VuZXJhdGVUaXRsZToge1xuICAgICAgICAgICAgICAgIG1vZGVsOiBzdW1tYXJ5TW9kZWwodGhpcy5jb25maWcpLFxuICAgICAgICAgICAgICAgIGluc3RydWN0aW9uczogVElUTEVfSU5TVFJVQ1RJT05TLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfVxuICAgICAgICAgIDoge30pLFxuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGRTdG9yYWdlKGFnZW50SWQ6IHN0cmluZywgc2V0dGluZzogU3RvcmFnZVNldHRpbmcpOiBQb3N0Z3Jlc1N0b3JlIHwgdW5kZWZpbmVkIHtcbiAgICBpZiAoIXNldHRpbmcpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgaWYgKHR5cGVvZiBzZXR0aW5nID09PSBcImJvb2xlYW5cIikge1xuICAgICAgcmV0dXJuIG5ldyBQb3N0Z3Jlc1N0b3JlKHtcbiAgICAgICAgaWQ6IGBtYXN0cmEtc3RvcmVfXyR7YWdlbnRJZH1gLFxuICAgICAgICBzY2hlbWFOYW1lOiBhZ2VudFN0b3JhZ2VTY2hlbWFOYW1lKGFnZW50SWQpLFxuICAgICAgICBwb29sOiB0aGlzLnNlcnZpY2VQcmluY2lwYWxQb29sLFxuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIENhc3Q6IGB3aXRoSWRgIGd1YXJhbnRlZXMgYGlkYCBpcyBzZXQsIGJ1dCB0aGUgZGlzdHJpYnV0aXZlXG4gICAgLy8gT21pdCArIGBpZD86IHN0cmluZ2Agc2hhcGUgZG9lc24ndCBzdHJ1Y3R1cmFsbHkgbmFycm93IHRvIHRoZVxuICAgIC8vIGRpc2NyaW1pbmF0ZWQgdW5pb24gbWVtYmVycy4gUnVudGltZSBzaGFwZSBpcyBpZGVudGljYWwuXG4gICAgcmV0dXJuIG5ldyBQb3N0Z3Jlc1N0b3JlKFxuICAgICAgd2l0aElkKHNldHRpbmcsIGBtYXN0cmEtc3RvcmVfXyR7YWdlbnRJZH1gKSBhcyBDb25zdHJ1Y3RvclBhcmFtZXRlcnM8dHlwZW9mIFBvc3RncmVzU3RvcmU+WzBdLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZSB0aGUgYWdlbnQncyB2ZWN0b3Igc3RvcmUuIENhc2NhZGU6XG4gICAqXG4gICAqIC0gZmFsc3k6IG5vIHZlY3Rvci5cbiAgICogLSBgYm9vbGVhbmAgLyBgdW5kZWZpbmVkLWluaGVyaXRpbmctdHJ1ZWA6IHJldHVybiB0aGUgc2hhcmVkXG4gICAqICAgc2luZ2xldG9uIChidWlsdCBsYXppbHkgb24gZmlyc3QgY2FsbCkuIEFsbCBhZ2VudHMgdGhhdFxuICAgKiAgIGRlZmF1bHQtZW5hYmxlIG1lbW9yeSB3cml0ZSBpbnRvIGFuZCByZWNhbGwgZnJvbSBvbmUgaW5kZXguXG4gICAqIC0gb2JqZWN0OiBidWlsZCBhIGRlZGljYXRlZCBgUGdWZWN0b3JgIGZvciB0aGlzIGFnZW50LlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFZlY3RvcihzZXR0aW5nOiBNZW1vcnlTZXR0aW5nKTogUGdWZWN0b3IgfCB1bmRlZmluZWQge1xuICAgIGlmICghc2V0dGluZykgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBpZiAodHlwZW9mIHNldHRpbmcgPT09IFwiYm9vbGVhblwiKSByZXR1cm4gdGhpcy5nZXRTaGFyZWRWZWN0b3IoKTtcbiAgICByZXR1cm4gYnVpbGRQZ1ZlY3RvcihzZXR0aW5nKTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0U2hhcmVkVmVjdG9yKCk6IFBnVmVjdG9yIHtcbiAgICBpZiAoIXRoaXMuc2hhcmVkVmVjdG9yKSB7XG4gICAgICB0aGlzLnNoYXJlZFZlY3RvciA9IGJ1aWxkU2hhcmVkUGdWZWN0b3IodGhpcy5zZXJ2aWNlUHJpbmNpcGFsUG9vbCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnNoYXJlZFZlY3RvcjtcbiAgfVxufVxuXG4vKipcbiAqIEJ1aWxkIHRoZSBzaGFyZWQgYFBnVmVjdG9yYCB0aGF0IGJhY2tzIHRoZSBkZWZhdWx0XG4gKiBgZGVmLm1lbW9yeSA9PT0gdHJ1ZWAgY2FzZSBhY3Jvc3MgZXZlcnkgYWdlbnQuXG4gKlxuICogYFBnVmVjdG9yYCdzIGNvbnN0cnVjdG9yIGFjY2VwdHMgb25seSBjb25uZWN0aW9uLXN0eWxlIGNvbmZpZ3NcbiAqIChgSG9zdENvbmZpZ2AgLyBgQ29ubmVjdGlvblN0cmluZ0NvbmZpZ2AgLyBgQ2xpZW50Q29uZmlnYCk7IHRoZXJlIGlzXG4gKiBubyBgeyBwb29sIH1gIHNob3J0aGFuZCB0aGUgd2F5IGBQb3N0Z3Jlc1N0b3JlYCBoYXMgb25lLiBXb3JzZSwgdGhlXG4gKiBjb25zdHJ1Y3RvciBzeW5jaHJvbm91c2x5IGtpY2tzIG9mZiBhIGBjYWNoZVdhcm11cFByb21pc2VgIElJRkUgdGhhdFxuICogY2FsbHMgYHRoaXMucG9vbC5jb25uZWN0KClgIGJlZm9yZSByZXR1cm5pbmcsIHNvIHdlIGNhbid0IGNsZWFubHlcbiAqIGhhbmQgaXQgYW4gaW5lcnQgY29uZmlnIGFuZCBwYXRjaCB0aGUgcG9vbCBhZnRlcndhcmRzLlxuICpcbiAqIFRoZSB0cmljazogcGFzcyBpbGxlZ2FsLWJ1dC12YWxpZGF0aW9uLXBhc3NpbmcgcGxhY2Vob2xkZXJzIHNvIHRoZVxuICogd2FybXVwJ3MgYG5ldC5jb25uZWN0KClgIHJlamVjdHMgc3luY2hyb25vdXNseSB3aXRoIGBSYW5nZUVycm9yYFxuICogKE5vZGUgdmFsaWRhdGVzIGAwIDw9IHBvcnQgPCA2NTUzNmApLiBUaGUgSUlGRSdzIGBjYXRjaCB7fWAgc3dhbGxvd3NcbiAqIGl0LCBubyBETlMgbG9va3VwIG9yIFRDUCBhdHRlbXB0IGhhcHBlbnMsIGFuZCB3ZSB0aGVuIHN3YXBcbiAqIGBwZ1ZlY3Rvci5wb29sYCB0byB0aGUgbGFrZWJhc2UgcG9vbC4gRXZlcnkgc3Vic2VxdWVudCBgUGdWZWN0b3JgXG4gKiBtZXRob2QgcmVhZHMgYHRoaXMucG9vbGAgYXQgY2FsbCB0aW1lLCBzbyBhbGwgcmVhbCBJL08gZ29lcyB0aHJvdWdoXG4gKiB0aGUgbGFrZWJhc2UgcG9vbCBmcm9tIHRoZW4gb24uIFRoZSBwbGFjZWhvbGRlciBwb29sIGlzIGAuZW5kKClgJ2RcbiAqIHNvIGl0cyBzb2NrZXQgYm9vay1rZWVwaW5nIGlzIHJlbGVhc2VkLlxuICovXG5mdW5jdGlvbiBidWlsZFNoYXJlZFBnVmVjdG9yKHBvb2w6IFBvb2wpOiBQZ1ZlY3RvciB7XG4gIGNvbnN0IHZlY3RvciA9IG5ldyBQZ1ZlY3Rvcih7XG4gICAgaWQ6IGBwZyR7aGFzaC5pZCgpfWAsXG4gICAgLy8gS2VlcCB0aGUgcmVjYWxsIGluZGV4IG91dCBvZiBgcHVibGljYDogb24gYSBMYWtlYmFzZSBkYXRhYmFzZSB0aGUgYXBwXG4gICAgLy8gc2VydmljZSBwcmluY2lwYWwgaGFzIG5vIENSRUFURSBvbiBgcHVibGljYCAoUEcxNSsgbG9ja3MgaXQgZG93biksIHNvIGFcbiAgICAvLyBkZWZhdWx0LXNjaGVtYSBQZ1ZlY3RvciBmYWlscyBvbiBDUkVBVEUgSU5ERVggd2l0aCBcInBlcm1pc3Npb24gZGVuaWVkIGZvclxuICAgIC8vIHNjaGVtYSBwdWJsaWNcIi4gUGdWZWN0b3IgcnVucyBDUkVBVEUgU0NIRU1BIElGIE5PVCBFWElTVFMgZm9yIGEgbmFtZWRcbiAgICAvLyBzY2hlbWEsIHNvIHRoZSBTUCBjcmVhdGVzIGFuZCBvd25zIGBtYXN0cmFfaW5zdGFuY2VgICh0aGUgc2FtZVxuICAgIC8vIGluc3RhbmNlLWxldmVsIHNjaGVtYSB0aGUgd29ya2Zsb3ctc25hcHNob3Qgc3RvcmUgdXNlcykgYW5kIHRoZSBpbmRleFxuICAgIC8vIGxhbmRzIHRoZXJlLiBUYWJsZSBuYW1lcyBkb24ndCBjb2xsaWRlIHdpdGggdGhlIHN0b3JhZ2UgdGFibGVzLlxuICAgIHNjaGVtYU5hbWU6IFwibWFzdHJhX2luc3RhbmNlXCIsXG4gICAgaG9zdDogXCItMVwiLFxuICAgIHBvcnQ6IC0xLFxuICAgIGRhdGFiYXNlOiBcIl9cIixcbiAgICB1c2VyOiBcIl9cIixcbiAgICBwYXNzd29yZDogXCJfXCIsXG4gIH0pO1xuICBjb25zdCBwbGFjZWhvbGRlciA9IHZlY3Rvci5wb29sO1xuICB2ZWN0b3IucG9vbCA9IHBvb2w7XG4gIHZvaWQgcGxhY2Vob2xkZXIuZW5kKCkuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgcmV0dXJuIHZlY3Rvcjtcbn1cblxuLyoqIFBlci1hZ2VudCBkZWRpY2F0ZWQgYFBnVmVjdG9yYCAocmFyZTsgb3B0LWluIHZpYSBvYmplY3Qgb3ZlcnJpZGUpLiAqL1xuZnVuY3Rpb24gYnVpbGRQZ1ZlY3RvcihzZXR0aW5nOiBNYXN0cmFNZW1vcnlDb25maWdPdmVycmlkZSk6IFBnVmVjdG9yIHtcbiAgcmV0dXJuIG5ldyBQZ1ZlY3RvcihcbiAgICB3aXRoSWQoc2V0dGluZywgYHBnLXZlY3Rvcl9fJHtoYXNoLmlkKCl9YCkgYXMgQ29uc3RydWN0b3JQYXJhbWV0ZXJzPHR5cGVvZiBQZ1ZlY3Rvcj5bMF0sXG4gICk7XG59XG5cbi8qKiBUcnVlIHdoZW4gdGhpcyBzZXR0aW5nIHJlcXVpcmVzIHRoZSBzaGFyZWQgTGFrZWJhc2UgcG9vbC4gKi9cbmZ1bmN0aW9uIHNldHRpbmdOZWVkc1NoYXJlZFBvb2woc2V0dGluZzogU3RvcmFnZVNldHRpbmcgfCBNZW1vcnlTZXR0aW5nIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gIHJldHVybiBzZXR0aW5nID09PSB0cnVlO1xufVxuXG4vKiogV2FsayB0aGUgdGhyZWUgc2hhcGVzIG9mIGBjb25maWcuYWdlbnRzYCBpbnRvIGEgZmxhdCBsaXN0LiAqL1xuZnVuY3Rpb24gY29sbGVjdEFnZW50RGVmaW5pdGlvbnMoY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcpOiBNYXN0cmFBZ2VudERlZmluaXRpb25bXSB7XG4gIGNvbnN0IGFnZW50cyA9IGNvbmZpZy5hZ2VudHM7XG4gIGlmICghYWdlbnRzKSByZXR1cm4gW107XG4gIGlmIChBcnJheS5pc0FycmF5KGFnZW50cykpIHJldHVybiBhZ2VudHM7XG4gIGlmICh0eXBlb2YgKGFnZW50cyBhcyBNYXN0cmFBZ2VudERlZmluaXRpb24pLmluc3RydWN0aW9ucyA9PT0gXCJzdHJpbmdcIikge1xuICAgIHJldHVybiBbYWdlbnRzIGFzIE1hc3RyYUFnZW50RGVmaW5pdGlvbl07XG4gIH1cbiAgcmV0dXJuIE9iamVjdC52YWx1ZXMoYWdlbnRzIGFzIFJlY29yZDxzdHJpbmcsIE1hc3RyYUFnZW50RGVmaW5pdGlvbj4pO1xufVxuXG4vKiogRmlsbCBpbiBhIGRlZmF1bHQgYGlkYCB3aGVuIHRoZSBjYWxsZXIgZGlkbid0IHN1cHBseSBvbmUuICovXG5mdW5jdGlvbiB3aXRoSWQ8VCBleHRlbmRzIHsgaWQ/OiBzdHJpbmcgfT4odmFsdWU6IFQsIGZhbGxiYWNrOiBzdHJpbmcpOiBUIHtcbiAgcmV0dXJuIHZhbHVlLmlkID8gdmFsdWUgOiB7IC4uLnZhbHVlLCBpZDogZmFsbGJhY2sgfTtcbn1cbiJdfQ==
@@ -0,0 +1,63 @@
1
+ /**
2
+ * MLflow user-feedback logging: detect whether MLflow tracing is wired
3
+ * for this deployment, and log a thumbs / comment as a trace
4
+ * *assessment* via the Databricks MLflow REST API.
5
+ *
6
+ * Feedback attaches to a trace, and the plugin's spans reach MLflow
7
+ * through the same OTel pipeline as every other AppKit span (see
8
+ * `observability.ts`). MLflow derives its trace id from the OpenTelemetry
9
+ * trace id (`tr-<hex(otelTraceId)>`), so the server stamps the active
10
+ * trace id on each turn's response and the client sends it back here.
11
+ *
12
+ * There is no MLflow JS SDK, so this posts to the assessments REST
13
+ * endpoint directly using the OBO-scoped workspace client (the feedback
14
+ * is thus attributed to the signed-in user). Trace export is
15
+ * asynchronous, so the just-finished trace may not exist in MLflow yet
16
+ * when the user reacts; the log call retries briefly on "not found"
17
+ * before giving up softly.
18
+ *
19
+ * @module
20
+ */
21
+ import { appkit } from "@dbx-tools/appkit";
22
+ /** Workspace client carried on an AppKit execution context. */
23
+ type WorkspaceClient = appkit.WorkspaceClientLike;
24
+ /**
25
+ * Whether MLflow feedback logging is available for this deployment.
26
+ *
27
+ * Enabled when an OTLP exporter endpoint is configured (traces are
28
+ * actually shipped somewhere) AND an MLflow experiment is named - the
29
+ * two signals that the OTLP backend is MLflow and traces will
30
+ * materialize there. Both are standard env vars, so no plugin config is
31
+ * required; a deployment opts in simply by wiring MLflow tracing.
32
+ */
33
+ export declare function mlflowEnabled(): boolean;
34
+ /**
35
+ * Resolve whether user feedback is enabled from the plugin's optional
36
+ * `config.feedback` override: the explicit boolean wins, otherwise fall
37
+ * back to auto-detecting MLflow tracing ({@link mlflowEnabled}). Shared
38
+ * by the plugin's client-config gate and the server's trace-id header so
39
+ * the two never disagree.
40
+ */
41
+ export declare function resolveFeedbackEnabled(explicit: boolean | undefined): boolean;
42
+ /** Parameters for {@link logFeedback}. */
43
+ export interface LogFeedbackParams {
44
+ /** MLflow trace id the assessment attaches to (`tr-<hex>`). */
45
+ traceId: string;
46
+ /** Assessment name; defaults per whether a value or a comment is sent. */
47
+ name?: string;
48
+ /** Thumbs / rating / label value. Omit for a comment-only submission. */
49
+ value?: boolean | number | string;
50
+ /** Freeform comment: the rationale alongside a value, or the value itself when none. */
51
+ comment?: string;
52
+ /** Identity the feedback is attributed to (user email / id). */
53
+ sourceId?: string;
54
+ }
55
+ /**
56
+ * Log a HUMAN feedback assessment to a trace. Returns the created
57
+ * assessment id on success, or `undefined` when the trace can't be
58
+ * found (even after retrying for export lag) or the request otherwise
59
+ * fails - callers surface that as a soft "not recorded" rather than an
60
+ * error, keeping the chat usable.
61
+ */
62
+ export declare function logFeedback(client: WorkspaceClient, params: LogFeedbackParams): Promise<string | undefined>;
63
+ export {};
@@ -0,0 +1,117 @@
1
+ /**
2
+ * MLflow user-feedback logging: detect whether MLflow tracing is wired
3
+ * for this deployment, and log a thumbs / comment as a trace
4
+ * *assessment* via the Databricks MLflow REST API.
5
+ *
6
+ * Feedback attaches to a trace, and the plugin's spans reach MLflow
7
+ * through the same OTel pipeline as every other AppKit span (see
8
+ * `observability.ts`). MLflow derives its trace id from the OpenTelemetry
9
+ * trace id (`tr-<hex(otelTraceId)>`), so the server stamps the active
10
+ * trace id on each turn's response and the client sends it back here.
11
+ *
12
+ * There is no MLflow JS SDK, so this posts to the assessments REST
13
+ * endpoint directly using the OBO-scoped workspace client (the feedback
14
+ * is thus attributed to the signed-in user). Trace export is
15
+ * asynchronous, so the just-finished trace may not exist in MLflow yet
16
+ * when the user reacts; the log call retries briefly on "not found"
17
+ * before giving up softly.
18
+ *
19
+ * @module
20
+ */
21
+ import { async, error, log } from "@dbx-tools/shared-core";
22
+ import { feedback } from "@dbx-tools/shared-mastra";
23
+ import { databricksFetch, readResponseJson, readResponseText } from "./rest.js";
24
+ const logger = log.logger("mastra/mlflow");
25
+ /** Assessments REST path for a trace. `3.0` is the current MLflow API version. */
26
+ const assessmentsPath = (traceId) => `/api/3.0/mlflow/traces/${encodeURIComponent(traceId)}/assessments`;
27
+ /** Number of times to retry a "trace not found" response before giving up. */
28
+ const NOT_FOUND_RETRIES = 3;
29
+ /** Base backoff between "trace not found" retries, in ms (grows linearly). */
30
+ const NOT_FOUND_BACKOFF_MS = 1200;
31
+ /**
32
+ * Whether MLflow feedback logging is available for this deployment.
33
+ *
34
+ * Enabled when an OTLP exporter endpoint is configured (traces are
35
+ * actually shipped somewhere) AND an MLflow experiment is named - the
36
+ * two signals that the OTLP backend is MLflow and traces will
37
+ * materialize there. Both are standard env vars, so no plugin config is
38
+ * required; a deployment opts in simply by wiring MLflow tracing.
39
+ */
40
+ export function mlflowEnabled() {
41
+ const hasExporter = Boolean(process.env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim() ||
42
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT?.trim());
43
+ const hasExperiment = Boolean(process.env.MLFLOW_EXPERIMENT_ID?.trim() || process.env.MLFLOW_EXPERIMENT_NAME?.trim());
44
+ return hasExporter && hasExperiment;
45
+ }
46
+ /**
47
+ * Resolve whether user feedback is enabled from the plugin's optional
48
+ * `config.feedback` override: the explicit boolean wins, otherwise fall
49
+ * back to auto-detecting MLflow tracing ({@link mlflowEnabled}). Shared
50
+ * by the plugin's client-config gate and the server's trace-id header so
51
+ * the two never disagree.
52
+ */
53
+ export function resolveFeedbackEnabled(explicit) {
54
+ return explicit ?? mlflowEnabled();
55
+ }
56
+ /**
57
+ * Log a HUMAN feedback assessment to a trace. Returns the created
58
+ * assessment id on success, or `undefined` when the trace can't be
59
+ * found (even after retrying for export lag) or the request otherwise
60
+ * fails - callers surface that as a soft "not recorded" rather than an
61
+ * error, keeping the chat usable.
62
+ */
63
+ export async function logFeedback(client, params) {
64
+ // A comment with no thumbs value is logged as text feedback; a value
65
+ // (with an optional comment as the rationale) is the thumbs path.
66
+ const hasValue = params.value !== undefined;
67
+ const name = params.name?.trim() ||
68
+ (hasValue ? feedback.DEFAULT_FEEDBACK_NAME : feedback.DEFAULT_COMMENT_NAME);
69
+ const value = hasValue ? params.value : params.comment;
70
+ const assessment = {
71
+ trace_id: params.traceId,
72
+ assessment_name: name,
73
+ source: {
74
+ source_type: "HUMAN",
75
+ source_id: params.sourceId?.trim() || "user",
76
+ },
77
+ feedback: { value },
78
+ ...(hasValue && params.comment?.trim() ? { rationale: params.comment } : {}),
79
+ };
80
+ const body = { assessment };
81
+ for (let attempt = 0; attempt <= NOT_FOUND_RETRIES; attempt++) {
82
+ let res;
83
+ try {
84
+ res = await databricksFetch(client, assessmentsPath(params.traceId), {
85
+ method: "POST",
86
+ body,
87
+ });
88
+ }
89
+ catch (err) {
90
+ logger.warn("feedback request failed", {
91
+ traceId: params.traceId,
92
+ error: error.errorMessage(err),
93
+ });
94
+ return undefined;
95
+ }
96
+ if (res.ok) {
97
+ const parsed = await readResponseJson(res);
98
+ const assessmentId = parsed?.assessment?.assessment_id ??
99
+ parsed?.assessment_id;
100
+ return typeof assessmentId === "string" ? assessmentId : "";
101
+ }
102
+ // Trace export is async; a fresh trace may not exist yet. Retry a
103
+ // few times with a short backoff before giving up softly.
104
+ if (res.status === 404 && attempt < NOT_FOUND_RETRIES) {
105
+ await async.sleep(NOT_FOUND_BACKOFF_MS * (attempt + 1));
106
+ continue;
107
+ }
108
+ logger.warn("feedback not recorded", {
109
+ traceId: params.traceId,
110
+ status: res.status,
111
+ body: await readResponseText(res),
112
+ });
113
+ return undefined;
114
+ }
115
+ return undefined;
116
+ }
117
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWxmbG93LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21sZmxvdy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUdILE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzNELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsZUFBZSxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sUUFBUSxDQUFDO0FBRTdFLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7QUFLM0Msa0ZBQWtGO0FBQ2xGLE1BQU0sZUFBZSxHQUFHLENBQUMsT0FBZSxFQUFVLEVBQUUsQ0FDbEQsMEJBQTBCLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUM7QUFFdEUsOEVBQThFO0FBQzlFLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxDQUFDO0FBQzVCLDhFQUE4RTtBQUM5RSxNQUFNLG9CQUFvQixHQUFHLElBQUksQ0FBQztBQUVsQzs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxhQUFhO0lBQzNCLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FDekIsT0FBTyxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsRUFBRSxJQUFJLEVBQUU7UUFDL0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQ0FBa0MsRUFBRSxJQUFJLEVBQUUsQ0FDdkQsQ0FBQztJQUNGLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsRUFBRSxJQUFJLEVBQUUsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixFQUFFLElBQUksRUFBRSxDQUN2RixDQUFDO0lBQ0YsT0FBTyxXQUFXLElBQUksYUFBYSxDQUFDO0FBQ3RDLENBQUM7QUFFRDs7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dkIsTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNO1lBQ2xCLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDLEdBQUcsQ0FBQztTQUNsQyxDQUFDLENBQUM7UUFDSCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTUxmbG93IHVzZXItZmVlZGJhY2sgbG9nZ2luZzogZGV0ZWN0IHdoZXRoZXIgTUxmbG93IHRyYWNpbmcgaXMgd2lyZWRcbiAqIGZvciB0aGlzIGRlcGxveW1lbnQsIGFuZCBsb2cgYSB0aHVtYnMgLyBjb21tZW50IGFzIGEgdHJhY2VcbiAqICphc3Nlc3NtZW50KiB2aWEgdGhlIERhdGFicmlja3MgTUxmbG93IFJFU1QgQVBJLlxuICpcbiAqIEZlZWRiYWNrIGF0dGFjaGVzIHRvIGEgdHJhY2UsIGFuZCB0aGUgcGx1Z2luJ3Mgc3BhbnMgcmVhY2ggTUxmbG93XG4gKiB0aHJvdWdoIHRoZSBzYW1lIE9UZWwgcGlwZWxpbmUgYXMgZXZlcnkgb3RoZXIgQXBwS2l0IHNwYW4gKHNlZVxuICogYG9ic2VydmFiaWxpdHkudHNgKS4gTUxmbG93IGRlcml2ZXMgaXRzIHRyYWNlIGlkIGZyb20gdGhlIE9wZW5UZWxlbWV0cnlcbiAqIHRyYWNlIGlkIChgdHItPGhleChvdGVsVHJhY2VJZCk+YCksIHNvIHRoZSBzZXJ2ZXIgc3RhbXBzIHRoZSBhY3RpdmVcbiAqIHRyYWNlIGlkIG9uIGVhY2ggdHVybidzIHJlc3BvbnNlIGFuZCB0aGUgY2xpZW50IHNlbmRzIGl0IGJhY2sgaGVyZS5cbiAqXG4gKiBUaGVyZSBpcyBubyBNTGZsb3cgSlMgU0RLLCBzbyB0aGlzIHBvc3RzIHRvIHRoZSBhc3Nlc3NtZW50cyBSRVNUXG4gKiBlbmRwb2ludCBkaXJlY3RseSB1c2luZyB0aGUgT0JPLXNjb3BlZCB3b3Jrc3BhY2UgY2xpZW50ICh0aGUgZmVlZGJhY2tcbiAqIGlzIHRodXMgYXR0cmlidXRlZCB0byB0aGUgc2lnbmVkLWluIHVzZXIpLiBUcmFjZSBleHBvcnQgaXNcbiAqIGFzeW5jaHJvbm91cywgc28gdGhlIGp1c3QtZmluaXNoZWQgdHJhY2UgbWF5IG5vdCBleGlzdCBpbiBNTGZsb3cgeWV0XG4gKiB3aGVuIHRoZSB1c2VyIHJlYWN0czsgdGhlIGxvZyBjYWxsIHJldHJpZXMgYnJpZWZseSBvbiBcIm5vdCBmb3VuZFwiXG4gKiBiZWZvcmUgZ2l2aW5nIHVwIHNvZnRseS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgYXBwa2l0IH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG5pbXBvcnQgeyBhc3luYywgZXJyb3IsIGxvZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBmZWVkYmFjayB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1tYXN0cmFcIjtcbmltcG9ydCB7IGRhdGFicmlja3NGZXRjaCwgcmVhZFJlc3BvbnNlSnNvbiwgcmVhZFJlc3BvbnNlVGV4dCB9IGZyb20gXCIuL3Jlc3RcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcIm1hc3RyYS9tbGZsb3dcIik7XG5cbi8qKiBXb3Jrc3BhY2UgY2xpZW50IGNhcnJpZWQgb24gYW4gQXBwS2l0IGV4ZWN1dGlvbiBjb250ZXh0LiAqL1xudHlwZSBXb3Jrc3BhY2VDbGllbnQgPSBhcHBraXQuV29ya3NwYWNlQ2xpZW50TGlrZTtcblxuLyoqIEFzc2Vzc21lbnRzIFJFU1QgcGF0aCBmb3IgYSB0cmFjZS4gYDMuMGAgaXMgdGhlIGN1cnJlbnQgTUxmbG93IEFQSSB2ZXJzaW9uLiAqL1xuY29uc3QgYXNzZXNzbWVudHNQYXRoID0gKHRyYWNlSWQ6IHN0cmluZyk6IHN0cmluZyA9PlxuICBgL2FwaS8zLjAvbWxmbG93L3RyYWNlcy8ke2VuY29kZVVSSUNvbXBvbmVudCh0cmFjZUlkKX0vYXNzZXNzbWVudHNgO1xuXG4vKiogTnVtYmVyIG9mIHRpbWVzIHRvIHJldHJ5IGEgXCJ0cmFjZSBub3QgZm91bmRcIiByZXNwb25zZSBiZWZvcmUgZ2l2aW5nIHVwLiAqL1xuY29uc3QgTk9UX0ZPVU5EX1JFVFJJRVMgPSAzO1xuLyoqIEJhc2UgYmFja29mZiBiZXR3ZWVuIFwidHJhY2Ugbm90IGZvdW5kXCIgcmV0cmllcywgaW4gbXMgKGdyb3dzIGxpbmVhcmx5KS4gKi9cbmNvbnN0IE5PVF9GT1VORF9CQUNLT0ZGX01TID0gMTIwMDtcblxuLyoqXG4gKiBXaGV0aGVyIE1MZmxvdyBmZWVkYmFjayBsb2dnaW5nIGlzIGF2YWlsYWJsZSBmb3IgdGhpcyBkZXBsb3ltZW50LlxuICpcbiAqIEVuYWJsZWQgd2hlbiBhbiBPVExQIGV4cG9ydGVyIGVuZHBvaW50IGlzIGNvbmZpZ3VyZWQgKHRyYWNlcyBhcmVcbiAqIGFjdHVhbGx5IHNoaXBwZWQgc29tZXdoZXJlKSBBTkQgYW4gTUxmbG93IGV4cGVyaW1lbnQgaXMgbmFtZWQgLSB0aGVcbiAqIHR3byBzaWduYWxzIHRoYXQgdGhlIE9UTFAgYmFja2VuZCBpcyBNTGZsb3cgYW5kIHRyYWNlcyB3aWxsXG4gKiBtYXRlcmlhbGl6ZSB0aGVyZS4gQm90aCBhcmUgc3RhbmRhcmQgZW52IHZhcnMsIHNvIG5vIHBsdWdpbiBjb25maWcgaXNcbiAqIHJlcXVpcmVkOyBhIGRlcGxveW1lbnQgb3B0cyBpbiBzaW1wbHkgYnkgd2lyaW5nIE1MZmxvdyB0cmFjaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbWxmbG93RW5hYmxlZCgpOiBib29sZWFuIHtcbiAgY29uc3QgaGFzRXhwb3J0ZXIgPSBCb29sZWFuKFxuICAgIHByb2Nlc3MuZW52Lk9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVD8udHJpbSgpIHx8XG4gICAgcHJvY2Vzcy5lbnYuT1RFTF9FWFBPUlRFUl9PVExQX1RSQUNFU19FTkRQT0lOVD8udHJpbSgpLFxuICApO1xuICBjb25zdCBoYXNFeHBlcmltZW50ID0gQm9vbGVhbihcbiAgICBwcm9jZXNzLmVudi5NTEZMT1dfRVhQRVJJTUVOVF9JRD8udHJpbSgpIHx8IHByb2Nlc3MuZW52Lk1MRkxPV19FWFBFUklNRU5UX05BTUU/LnRyaW0oKSxcbiAgKTtcbiAgcmV0dXJuIGhhc0V4cG9ydGVyICYmIGhhc0V4cGVyaW1lbnQ7XG59XG5cbi8qKlxuICogUmVzb2x2ZSB3aGV0aGVyIHVzZXIgZmVlZGJhY2sgaXMgZW5hYmxlZCBmcm9tIHRoZSBwbHVnaW4ncyBvcHRpb25hbFxuICogYGNvbmZpZy5mZWVkYmFja2Agb3ZlcnJpZGU6IHRoZSBleHBsaWNpdCBib29sZWFuIHdpbnMsIG90aGVyd2lzZSBmYWxsXG4gKiBiYWNrIHRvIGF1dG8tZGV0ZWN0aW5nIE1MZmxvdyB0cmFjaW5nICh7QGxpbmsgbWxmbG93RW5hYmxlZH0pLiBTaGFyZWRcbiAqIGJ5IHRoZSBwbHVnaW4ncyBjbGllbnQtY29uZmlnIGdhdGUgYW5kIHRoZSBzZXJ2ZXIncyB0cmFjZS1pZCBoZWFkZXIgc29cbiAqIHRoZSB0d28gbmV2ZXIgZGlzYWdyZWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRmVlZGJhY2tFbmFibGVkKGV4cGxpY2l0OiBib29sZWFuIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gIHJldHVybiBleHBsaWNpdCA/PyBtbGZsb3dFbmFibGVkKCk7XG59XG5cbi8qKiBQYXJhbWV0ZXJzIGZvciB7QGxpbmsgbG9nRmVlZGJhY2t9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2dGZWVkYmFja1BhcmFtcyB7XG4gIC8qKiBNTGZsb3cgdHJhY2UgaWQgdGhlIGFzc2Vzc21lbnQgYXR0YWNoZXMgdG8gKGB0ci08aGV4PmApLiAqL1xuICB0cmFjZUlkOiBzdHJpbmc7XG4gIC8qKiBBc3Nlc3NtZW50IG5hbWU7IGRlZmF1bHRzIHBlciB3aGV0aGVyIGEgdmFsdWUgb3IgYSBjb21tZW50IGlzIHNlbnQuICovXG4gIG5hbWU/OiBzdHJpbmc7XG4gIC8qKiBUaHVtYnMgLyByYXRpbmcgLyBsYWJlbCB2YWx1ZS4gT21pdCBmb3IgYSBjb21tZW50LW9ubHkgc3VibWlzc2lvbi4gKi9cbiAgdmFsdWU/OiBib29sZWFuIHwgbnVtYmVyIHwgc3RyaW5nO1xuICAvKiogRnJlZWZvcm0gY29tbWVudDogdGhlIHJhdGlvbmFsZSBhbG9uZ3NpZGUgYSB2YWx1ZSwgb3IgdGhlIHZhbHVlIGl0c2VsZiB3aGVuIG5vbmUuICovXG4gIGNvbW1lbnQ/OiBzdHJpbmc7XG4gIC8qKiBJZGVudGl0eSB0aGUgZmVlZGJhY2sgaXMgYXR0cmlidXRlZCB0byAodXNlciBlbWFpbCAvIGlkKS4gKi9cbiAgc291cmNlSWQ/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogTG9nIGEgSFVNQU4gZmVlZGJhY2sgYXNzZXNzbWVudCB0byBhIHRyYWNlLiBSZXR1cm5zIHRoZSBjcmVhdGVkXG4gKiBhc3Nlc3NtZW50IGlkIG9uIHN1Y2Nlc3MsIG9yIGB1bmRlZmluZWRgIHdoZW4gdGhlIHRyYWNlIGNhbid0IGJlXG4gKiBmb3VuZCAoZXZlbiBhZnRlciByZXRyeWluZyBmb3IgZXhwb3J0IGxhZykgb3IgdGhlIHJlcXVlc3Qgb3RoZXJ3aXNlXG4gKiBmYWlscyAtIGNhbGxlcnMgc3VyZmFjZSB0aGF0IGFzIGEgc29mdCBcIm5vdCByZWNvcmRlZFwiIHJhdGhlciB0aGFuIGFuXG4gKiBlcnJvciwga2VlcGluZyB0aGUgY2hhdCB1c2FibGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2dGZWVkYmFjayhcbiAgY2xpZW50OiBXb3Jrc3BhY2VDbGllbnQsXG4gIHBhcmFtczogTG9nRmVlZGJhY2tQYXJhbXMsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICAvLyBBIGNvbW1lbnQgd2l0aCBubyB0aHVtYnMgdmFsdWUgaXMgbG9nZ2VkIGFzIHRleHQgZmVlZGJhY2s7IGEgdmFsdWVcbiAgLy8gKHdpdGggYW4gb3B0aW9uYWwgY29tbWVudCBhcyB0aGUgcmF0aW9uYWxlKSBpcyB0aGUgdGh1bWJzIHBhdGguXG4gIGNvbnN0IGhhc1ZhbHVlID0gcGFyYW1zLnZhbHVlICE9PSB1bmRlZmluZWQ7XG4gIGNvbnN0IG5hbWUgPVxuICAgIHBhcmFtcy5uYW1lPy50cmltKCkgfHxcbiAgICAoaGFzVmFsdWUgPyBmZWVkYmFjay5ERUZBVUxUX0ZFRURCQUNLX05BTUUgOiBmZWVkYmFjay5ERUZBVUxUX0NPTU1FTlRfTkFNRSk7XG4gIGNvbnN0IHZhbHVlID0gaGFzVmFsdWUgPyBwYXJhbXMudmFsdWUgOiBwYXJhbXMuY29tbWVudDtcbiAgY29uc3QgYXNzZXNzbWVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7XG4gICAgdHJhY2VfaWQ6IHBhcmFtcy50cmFjZUlkLFxuICAgIGFzc2Vzc21lbnRfbmFtZTogbmFtZSxcbiAgICBzb3VyY2U6IHtcbiAgICAgIHNvdXJjZV90eXBlOiBcIkhVTUFOXCIsXG4gICAgICBzb3VyY2VfaWQ6IHBhcmFtcy5zb3VyY2VJZD8udHJpbSgpIHx8IFwidXNlclwiLFxuICAgIH0sXG4gICAgZmVlZGJhY2s6IHsgdmFsdWUgfSxcbiAgICAuLi4oaGFzVmFsdWUgJiYgcGFyYW1zLmNvbW1lbnQ/LnRyaW0oKSA/IHsgcmF0aW9uYWxlOiBwYXJhbXMuY29tbWVudCB9IDoge30pLFxuICB9O1xuICBjb25zdCBib2R5ID0geyBhc3Nlc3NtZW50IH07XG5cbiAgZm9yIChsZXQgYXR0ZW1wdCA9IDA7IGF0dGVtcHQgPD0gTk9UX0ZPVU5EX1JFVFJJRVM7IGF0dGVtcHQrKykge1xuICAgIGxldCByZXM6IFJlc3BvbnNlO1xuICAgIHRyeSB7XG4gICAgICByZXMgPSBhd2FpdCBkYXRhYnJpY2tzRmV0Y2goY2xpZW50LCBhc3Nlc3NtZW50c1BhdGgocGFyYW1zLnRyYWNlSWQpLCB7XG4gICAgICAgIG1ldGhvZDogXCJQT1NUXCIsXG4gICAgICAgIGJvZHksXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGxvZ2dlci53YXJuKFwiZmVlZGJhY2sgcmVxdWVzdCBmYWlsZWRcIiwge1xuICAgICAgICB0cmFjZUlkOiBwYXJhbXMudHJhY2VJZCxcbiAgICAgICAgZXJyb3I6IGVycm9yLmVycm9yTWVzc2FnZShlcnIpLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBpZiAocmVzLm9rKSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBhd2FpdCByZWFkUmVzcG9uc2VKc29uKHJlcyk7XG4gICAgICBjb25zdCBhc3Nlc3NtZW50SWQgPVxuICAgICAgICAocGFyc2VkIGFzIHsgYXNzZXNzbWVudD86IHsgYXNzZXNzbWVudF9pZD86IHVua25vd24gfSB9KT8uYXNzZXNzbWVudD8uYXNzZXNzbWVudF9pZCA/P1xuICAgICAgICAocGFyc2VkIGFzIHsgYXNzZXNzbWVudF9pZD86IHVua25vd24gfSk/LmFzc2Vzc21lbnRfaWQ7XG4gICAgICByZXR1cm4gdHlwZW9mIGFzc2Vzc21lbnRJZCA9PT0gXCJzdHJpbmdcIiA/IGFzc2Vzc21lbnRJZCA6IFwiXCI7XG4gICAgfVxuICAgIC8vIFRyYWNlIGV4cG9ydCBpcyBhc3luYzsgYSBmcmVzaCB0cmFjZSBtYXkgbm90IGV4aXN0IHlldC4gUmV0cnkgYVxuICAgIC8vIGZldyB0aW1lcyB3aXRoIGEgc2hvcnQgYmFja29mZiBiZWZvcmUgZ2l2aW5nIHVwIHNvZnRseS5cbiAgICBpZiAocmVzLnN0YXR1cyA9PT0gNDA0ICYmIGF0dGVtcHQgPCBOT1RfRk9VTkRfUkVUUklFUykge1xuICAgICAgYXdhaXQgYXN5bmMuc2xlZXAoTk9UX0ZPVU5EX0JBQ0tPRkZfTVMgKiAoYXR0ZW1wdCArIDEpKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBsb2dnZXIud2FybihcImZlZWRiYWNrIG5vdCByZWNvcmRlZFwiLCB7XG4gICAgICB0cmFjZUlkOiBwYXJhbXMudHJhY2VJZCxcbiAgICAgIHN0YXR1czogcmVzLnN0YXR1cyxcbiAgICAgIGJvZHk6IGF3YWl0IHJlYWRSZXNwb25zZVRleHQocmVzKSxcbiAgICB9KTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG4iXX0=
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Databricks Model Serving resolver for Mastra agents.
3
+ *
4
+ * Each agent step calls {@link buildModel} with the active
5
+ * `RequestContext`. The user stamped by `MastraServer` carries an
6
+ * AppKit `WorkspaceClient`; we ask it for the workspace host and a
7
+ * fresh bearer header, then point Mastra's OpenAI-compatible provider
8
+ * at `/serving-endpoints` on that host.
9
+ *
10
+ * This module only adds the Mastra-specific glue. The actual model
11
+ * selection - listing the workspace catalogue and resolving an
12
+ * explicit name / class / fallback chain to a real endpoint id - lives
13
+ * in `@dbx-tools/model` ({@link selectModel}) so non-Mastra consumers
14
+ * (e.g. a job that just needs a model name) can reuse it. Here we
15
+ * assemble the explicit ask from Mastra's request context (the
16
+ * per-request override under {@link MASTRA_MODEL_OVERRIDE_KEY}, the
17
+ * agent / plugin `modelId`, or `DATABRICKS_SERVING_ENDPOINT_NAME`),
18
+ * pass the plugin's fuzzy / class / fallback knobs through, and wrap
19
+ * the resolved id in the OpenAI-compatible provider config Mastra
20
+ * expects. Catalogue fetches fail loud: network / auth errors
21
+ * propagate so callers see the real SDK message.
22
+ *
23
+ * @module
24
+ */
25
+ import { model } from "@dbx-tools/shared-model";
26
+ import type { MastraModelConfig } from "@mastra/core/llm";
27
+ import type { RequestContext } from "@mastra/core/request-context";
28
+ import { type MastraPluginConfig } from "./config.js";
29
+ type ModelClass = model.ModelClass;
30
+ /** Optional overrides accepted by {@link buildModel}. */
31
+ export interface BuildModelOverrides {
32
+ /**
33
+ * Static model id from the agent / plugin config (string sugar on
34
+ * `def.model` or `config.defaultModel`). Loses to the per-request
35
+ * override but wins over env / class / fallback.
36
+ */
37
+ modelId?: string;
38
+ /**
39
+ * Chat capability class to resolve when no explicit model id is
40
+ * supplied. Used by internal agents (e.g. the chart planner asks for
41
+ * {@link model.ModelClass.ChatFast}) to express intent without pinning an
42
+ * endpoint name; the live catalogue is classified and the top
43
+ * available model in the class is chosen, falling back to the
44
+ * class's static list when the workspace has none.
45
+ */
46
+ modelClass?: ModelClass;
47
+ }
48
+ /**
49
+ * Resolve a `MastraModelConfig` for the current agent step. Runs
50
+ * while `agent.stream` is inside the `asUser(req)` scope so tokens
51
+ * are user-scoped; outside an active user context the workspace
52
+ * client falls back to the service principal.
53
+ *
54
+ * Endpoint precedence: the per-request override
55
+ * ({@link MASTRA_MODEL_OVERRIDE_KEY}, only when `config.modelOverride` allows
56
+ * it), then {@link BuildModelOverrides.modelId} from the agent / plugin
57
+ * config, then `DATABRICKS_SERVING_ENDPOINT_NAME`. With none of those set the
58
+ * capability class and fallback ladder in `@dbx-tools/model` choose the
59
+ * endpoint.
60
+ */
61
+ export declare function buildModel(config: MastraPluginConfig, requestContext: RequestContext, overrides?: BuildModelOverrides): Promise<MastraModelConfig>;
62
+ export {};