@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.
- package/lib/index.d.ts +71 -0
- package/lib/index.js +56 -0
- package/lib/src/agents.d.ts +347 -0
- package/lib/src/agents.js +554 -0
- package/lib/src/chart.d.ts +192 -0
- package/lib/src/chart.js +638 -0
- package/lib/src/config.d.ts +479 -0
- package/lib/src/config.js +190 -0
- package/lib/src/defaults.d.ts +68 -0
- package/lib/src/defaults.js +107 -0
- package/lib/src/filesystems.d.ts +208 -0
- package/lib/src/filesystems.js +958 -0
- package/lib/src/genie.d.ts +166 -0
- package/lib/src/genie.js +969 -0
- package/lib/src/history.d.ts +97 -0
- package/lib/src/history.js +264 -0
- package/lib/src/mcp.d.ts +66 -0
- package/lib/src/mcp.js +65 -0
- package/lib/src/memory.d.ts +111 -0
- package/lib/src/memory.js +275 -0
- package/lib/src/mlflow.d.ts +63 -0
- package/lib/src/mlflow.js +117 -0
- package/lib/src/model.d.ts +62 -0
- package/lib/src/model.js +168 -0
- package/lib/src/observability.d.ts +81 -0
- package/lib/src/observability.js +98 -0
- package/lib/src/pagination.d.ts +23 -0
- package/lib/src/pagination.js +31 -0
- package/lib/src/plugin.d.ts +352 -0
- package/lib/src/plugin.js +1015 -0
- package/lib/src/processors.d.ts +62 -0
- package/lib/src/processors.js +162 -0
- package/lib/src/rest.d.ts +36 -0
- package/lib/src/rest.js +46 -0
- package/lib/src/server.d.ts +155 -0
- package/lib/src/server.js +336 -0
- package/lib/src/serving-sanitize.d.ts +104 -0
- package/lib/src/serving-sanitize.js +228 -0
- package/lib/src/serving.d.ts +61 -0
- package/lib/src/serving.js +78 -0
- package/lib/src/statement.d.ts +51 -0
- package/lib/src/statement.js +83 -0
- package/lib/src/storage-schema.d.ts +14 -0
- package/lib/src/storage-schema.js +34 -0
- package/lib/src/summarize.d.ts +70 -0
- package/lib/src/summarize.js +142 -0
- package/lib/src/threads.d.ts +109 -0
- package/lib/src/threads.js +301 -0
- package/lib/src/validation.d.ts +19 -0
- package/lib/src/validation.js +17 -0
- package/lib/src/workspaces.d.ts +68 -0
- package/lib/src/workspaces.js +246 -0
- package/lib/src/writer.d.ts +25 -0
- package/lib/src/writer.js +40 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +17 -13
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repairs traffic between Mastra / the AI SDK and Databricks Model Serving's
|
|
3
|
+
* OpenAI-compatible `/chat/completions` route, in both directions.
|
|
4
|
+
*
|
|
5
|
+
* Outbound ({@link rewriteServingBody}), because the transcript Mastra
|
|
6
|
+
* persists is not always a transcript the provider will accept back:
|
|
7
|
+
* Databricks-hosted Claude rejects replayed extended-thinking blocks and reads
|
|
8
|
+
* a trailing assistant message as a prefill request.
|
|
9
|
+
*
|
|
10
|
+
* Inbound ({@link rewriteServingResponseBody}), because Databricks-hosted
|
|
11
|
+
* Gemini answers with its native content-parts array where the OpenAI contract
|
|
12
|
+
* (and therefore the AI SDK's response schema) requires a plain string.
|
|
13
|
+
*
|
|
14
|
+
* Every repair here is a provider quirk rather than a schema violation, so all
|
|
15
|
+
* of them are applied on the wire (see the `globalThis.fetch` wrapper in
|
|
16
|
+
* `model.ts`) rather than by changing what the agent stores or what the UI
|
|
17
|
+
* shows.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { json, string } from "@dbx-tools/shared-core";
|
|
22
|
+
import { openaiChat, openaiResponses, } from "@dbx-tools/shared-model";
|
|
23
|
+
// Shared with the Responses sanitize path so both wire surfaces agree on what
|
|
24
|
+
// counts as a signed reasoning block.
|
|
25
|
+
const REASONING_PART_TYPES = openaiResponses.REASONING_TYPES;
|
|
26
|
+
/**
|
|
27
|
+
* Parse, sanitize, and re-serialize a `/serving-endpoints/...` POST
|
|
28
|
+
* body. Returns the original string verbatim when the body is not
|
|
29
|
+
* JSON or no rewrite was needed.
|
|
30
|
+
*/
|
|
31
|
+
export function rewriteServingBody(body) {
|
|
32
|
+
const parsed = json.parseRecord(body);
|
|
33
|
+
if (!parsed)
|
|
34
|
+
return body;
|
|
35
|
+
// Runs regardless of `messages`: Databricks refuses to parse a body carrying
|
|
36
|
+
// an unknown top-level field, so this failure is not specific to a transcript.
|
|
37
|
+
let changed = openaiChat.stripUnsupportedChatFields(parsed).length > 0;
|
|
38
|
+
if (Array.isArray(parsed.messages)) {
|
|
39
|
+
const messages = parsed.messages;
|
|
40
|
+
// Evaluated eagerly, not short-circuited: one transcript can need both
|
|
41
|
+
// reasoning blocks stripped AND a trailing assistant prefill folded back.
|
|
42
|
+
const stripped = stripReasoningFromServingMessages(messages);
|
|
43
|
+
const repaired = repairAssistantPrefill(messages);
|
|
44
|
+
changed = changed || stripped || repaired;
|
|
45
|
+
}
|
|
46
|
+
return changed ? JSON.stringify(parsed) : body;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Drop extended-thinking / reasoning blocks from a replayed transcript.
|
|
50
|
+
*
|
|
51
|
+
* Hybrid Claude endpoints (e.g. Sonnet 4.5+) may emit `reasoning` content
|
|
52
|
+
* parts on the first turn. Mastra persists and replays them on the next
|
|
53
|
+
* agent step, but Databricks-hosted Claude rejects those blocks on
|
|
54
|
+
* multi-turn tool continuations. The UI already captured reasoning for
|
|
55
|
+
* display; stripping here keeps provider replay compatible without
|
|
56
|
+
* changing what users see in the chat bubble.
|
|
57
|
+
*/
|
|
58
|
+
export function stripReasoningFromServingMessages(messages) {
|
|
59
|
+
let changed = false;
|
|
60
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
61
|
+
const msg = messages[i];
|
|
62
|
+
if (!msg)
|
|
63
|
+
continue;
|
|
64
|
+
if (msg.role === "reasoning") {
|
|
65
|
+
messages.splice(i, 1);
|
|
66
|
+
changed = true;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (msg.reasoning !== undefined) {
|
|
70
|
+
delete msg.reasoning;
|
|
71
|
+
changed = true;
|
|
72
|
+
}
|
|
73
|
+
if (msg.reasoning_content !== undefined) {
|
|
74
|
+
delete msg.reasoning_content;
|
|
75
|
+
changed = true;
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(msg.content))
|
|
78
|
+
continue;
|
|
79
|
+
const filtered = msg.content.filter((part) => {
|
|
80
|
+
const type = part?.type;
|
|
81
|
+
if (typeof type === "string" && REASONING_PART_TYPES.has(type)) {
|
|
82
|
+
changed = true;
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
});
|
|
87
|
+
if (filtered.length !== msg.content.length) {
|
|
88
|
+
msg.content = filtered;
|
|
89
|
+
}
|
|
90
|
+
const hasToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;
|
|
91
|
+
if (msg.role === "assistant" && !hasToolCalls && isEmptyServingContent(msg.content)) {
|
|
92
|
+
messages.splice(i, 1);
|
|
93
|
+
changed = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return changed;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Repair a Mastra/AI SDK message replay that Databricks-hosted Claude
|
|
100
|
+
* rejects with `"This model does not support assistant message
|
|
101
|
+
* prefill. The conversation must end with a user message."`.
|
|
102
|
+
*
|
|
103
|
+
* The bug pattern: when an assistant turn streams text *and* a
|
|
104
|
+
* `tool_call`, the AI SDK persists them as two separate assistant
|
|
105
|
+
* entries (text-only and tool-call-only). On the next agent step the
|
|
106
|
+
* tool-call entry is replayed *before* the tool result and the
|
|
107
|
+
* text entry is replayed *after* it, so the conversation ends with a
|
|
108
|
+
* trailing assistant text message. Anthropic interprets that as a
|
|
109
|
+
* prefill request and rejects it on Databricks (the upstream Bedrock
|
|
110
|
+
* route disallows prefill).
|
|
111
|
+
*
|
|
112
|
+
* Fix: when the last message is an assistant text with no `tool_calls`
|
|
113
|
+
* and the chain immediately before it is `assistant(tool_calls=...)`
|
|
114
|
+
* followed only by `tool(...)` results, fold the trailing text back
|
|
115
|
+
* into the `content` of that opening assistant and drop the duplicate.
|
|
116
|
+
*/
|
|
117
|
+
export function repairAssistantPrefill(messages) {
|
|
118
|
+
if (messages.length < 2)
|
|
119
|
+
return false;
|
|
120
|
+
const last = messages[messages.length - 1];
|
|
121
|
+
if (!last || last.role !== "assistant" || (last.tool_calls && last.tool_calls.length > 0)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
let i = messages.length - 2;
|
|
125
|
+
while (i >= 0 && messages[i]?.role === "tool")
|
|
126
|
+
i--;
|
|
127
|
+
if (i < 0)
|
|
128
|
+
return false;
|
|
129
|
+
const opener = messages[i];
|
|
130
|
+
if (!opener ||
|
|
131
|
+
opener.role !== "assistant" ||
|
|
132
|
+
!opener.tool_calls ||
|
|
133
|
+
opener.tool_calls.length === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const merged = [
|
|
137
|
+
string.trimToNull(textFromServingContent(opener.content)),
|
|
138
|
+
string.trimToNull(textFromServingContent(last.content)),
|
|
139
|
+
]
|
|
140
|
+
.filter((s) => s !== null)
|
|
141
|
+
.join("\n\n");
|
|
142
|
+
opener.content = merged;
|
|
143
|
+
messages.pop();
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Flatten content to the prose a human would read: text parts only, separated
|
|
148
|
+
* by blank lines, since the result is spliced into another turn's message body.
|
|
149
|
+
*/
|
|
150
|
+
function textFromServingContent(content) {
|
|
151
|
+
return openaiChat.chatContentToText(content, { separator: "\n\n", types: ["text"] });
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Whether a turn carries nothing worth replaying. Deliberately stricter than
|
|
155
|
+
* "has no text": a part of any other type (an image, a cache marker) counts as
|
|
156
|
+
* content, so a message holding one is kept even though it has no prose.
|
|
157
|
+
*/
|
|
158
|
+
function isEmptyServingContent(content) {
|
|
159
|
+
if (content === undefined)
|
|
160
|
+
return true;
|
|
161
|
+
if (typeof content === "string")
|
|
162
|
+
return content.trim().length === 0;
|
|
163
|
+
if (!Array.isArray(content))
|
|
164
|
+
return true;
|
|
165
|
+
return content.every((part) => {
|
|
166
|
+
if (part?.type === "text") {
|
|
167
|
+
return typeof part.text !== "string" || part.text.trim().length === 0;
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Parse, sanitize, and re-serialize a `/serving-endpoints/...` non-streaming
|
|
174
|
+
* JSON RESPONSE body. Returns the original string verbatim when the body is
|
|
175
|
+
* not JSON or no rewrite was needed, mirroring
|
|
176
|
+
* {@link rewriteServingBody} on the request side.
|
|
177
|
+
*/
|
|
178
|
+
export function rewriteServingResponseBody(body) {
|
|
179
|
+
const parsed = json.parseRecord(body);
|
|
180
|
+
if (!parsed)
|
|
181
|
+
return body;
|
|
182
|
+
return flattenChoiceMessageContent(parsed) ? JSON.stringify(parsed) : body;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Collapse a structured `choices[].message.content` array to the plain string
|
|
186
|
+
* the OpenAI Chat Completions contract specifies.
|
|
187
|
+
*
|
|
188
|
+
* Databricks-hosted Gemini answers a non-streaming `/chat/completions` call
|
|
189
|
+
* with the Gemini-native parts shape:
|
|
190
|
+
*
|
|
191
|
+
* ```json
|
|
192
|
+
* "content": [{ "type": "text", "text": "...", "thoughtSignature": "..." }]
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* `@ai-sdk/openai-compatible` validates the response against the OpenAI
|
|
196
|
+
* schema, where `content` is a nullable string, so it throws
|
|
197
|
+
* `Type validation failed: ... expected string, received array` and the whole
|
|
198
|
+
* call fails (`AI_APICallError: Invalid JSON response` on an HTTP 200). Mastra
|
|
199
|
+
* uses `doGenerate` for its side calls, so the visible symptom is
|
|
200
|
+
* `Error generating title` - every thread keeps its placeholder name.
|
|
201
|
+
*
|
|
202
|
+
* Flattening on the wire keeps the repair in one place: the streaming path is
|
|
203
|
+
* unaffected (deltas already carry string content), and neither the agent's
|
|
204
|
+
* stored transcript nor the UI has to know the provider emitted parts. Any
|
|
205
|
+
* non-text part (a `thoughtSignature`-only entry, an inline image) contributes
|
|
206
|
+
* nothing, matching {@link openaiChat.chatContentToText}, and an all-parts-empty
|
|
207
|
+
* message flattens to `""` rather than being dropped, so `finish_reason` and
|
|
208
|
+
* `usage` still round-trip.
|
|
209
|
+
*/
|
|
210
|
+
export function flattenChoiceMessageContent(payload) {
|
|
211
|
+
if (!Array.isArray(payload.choices))
|
|
212
|
+
return false;
|
|
213
|
+
let changed = false;
|
|
214
|
+
for (const choice of payload.choices) {
|
|
215
|
+
if (!choice || typeof choice !== "object")
|
|
216
|
+
continue;
|
|
217
|
+
const message = choice.message;
|
|
218
|
+
if (!message || typeof message !== "object")
|
|
219
|
+
continue;
|
|
220
|
+
const target = message;
|
|
221
|
+
if (!Array.isArray(target.content))
|
|
222
|
+
continue;
|
|
223
|
+
target.content = openaiChat.chatContentToText(target.content, { types: ["text"] });
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
226
|
+
return changed;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmluZy1zYW5pdGl6ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aW5nLXNhbml0aXplLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBRUgsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN0RCxPQUFPLEVBR0wsVUFBVSxFQUNWLGVBQWUsR0FDaEIsTUFBTSx5QkFBeUIsQ0FBQztBQWVqQyw4RUFBOEU7QUFDOUUsc0NBQXNDO0FBQ3RDLE1BQU0sb0JBQW9CLEdBQUcsZUFBZSxDQUFDLGVBQWUsQ0FBQztBQUU3RDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLElBQVk7SUFDN0MsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QyxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXpCLDZFQUE2RTtJQUM3RSwrRUFBK0U7SUFDL0UsSUFBSSxPQUFPLEdBQUcsVUFBVSxDQUFDLDBCQUEwQixDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFFdkUsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ25DLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFnQyxDQUFDO1FBQ3pELHVFQUF1RTtRQUN2RSwwRUFBMEU7UUFDMUUsTUFBTSxRQUFRLEdBQUcsaUNBQWlDLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDN0QsTUFBTSxRQUFRLEdBQUcsc0JBQXNCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDbEQsT0FBTyxHQUFHLE9BQU8sSUFBSSxRQUFRLElBQUksUUFBUSxDQUFDO0lBQzVDLENBQUM7SUFFRCxPQUFPLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0FBQ2pELENBQUM7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLFVBQVUsaUNBQWlDLENBQUMsUUFBOEI7SUFDOUUsSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFDO0lBQ3BCLEtBQUssSUFBSSxDQUFDLEdBQUcsUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzlDLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4QixJQUFJLENBQUMsR0FBRztZQUFFLFNBQVM7UUFDbkIsSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQzdCLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3RCLE9BQU8sR0FBRyxJQUFJLENBQUM7WUFDZixTQUFTO1FBQ1gsQ0FBQztRQUNELElBQUksR0FBRyxDQUFDLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNoQyxPQUFPLEdBQUcsQ0FBQyxTQUFTLENBQUM7WUFDckIsT0FBTyxHQUFHLElBQUksQ0FBQztRQUNqQixDQUFDO1FBQ0QsSUFBSSxHQUFHLENBQUMsaUJBQWlCLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDeEMsT0FBTyxHQUFHLENBQUMsaUJBQWlCLENBQUM7WUFDN0IsT0FBTyxHQUFHLElBQUksQ0FBQztRQUNqQixDQUFDO1FBQ0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQztZQUFFLFNBQVM7UUFDMUMsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtZQUMzQyxNQUFNLElBQUksR0FBRyxJQUFJLEVBQUUsSUFBSSxDQUFDO1lBQ3hCLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO2dCQUMvRCxPQUFPLEdBQUcsSUFBSSxDQUFDO2dCQUNmLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztZQUNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUMzQyxHQUFHLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQztRQUN6QixDQUFDO1FBQ0QsTUFBTSxZQUFZLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksR0FBRyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ2hGLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxXQUFXLElBQUksQ0FBQyxZQUFZLElBQUkscUJBQXFCLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDcEYsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDdEIsT0FBTyxHQUFHLElBQUksQ0FBQztRQUNqQixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQixDQUFDLFFBQThCO0lBQ25FLElBQUksUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDdEMsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDM0MsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFdBQVcsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMxRixPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxJQUFJLENBQUMsR0FBRyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUM1QixPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksS0FBSyxNQUFNO1FBQUUsQ0FBQyxFQUFFLENBQUM7SUFDbkQsSUFBSSxDQUFDLEdBQUcsQ0FBQztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3hCLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMzQixJQUNFLENBQUMsTUFBTTtRQUNQLE1BQU0sQ0FBQyxJQUFJLEtBQUssV0FBVztRQUMzQixDQUFDLE1BQU0sQ0FBQyxVQUFVO1FBQ2xCLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxLQUFLLENBQUMsRUFDOUIsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHO1FBQ2IsTUFBTSxDQUFDLFVBQVUsQ0FBQyxzQkFBc0IsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDekQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDeEQ7U0FDRSxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQWUsRUFBRSxDQUFDLENBQUMsS0FBSyxJQUFJLENBQUM7U0FDdEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2hCLE1BQU0sQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNmLE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQVMsc0JBQXNCLENBQUMsT0FBc0M7SUFDcEUsT0FBTyxVQUFVLENBQUMsaUJBQWlCLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDdkYsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLHFCQUFxQixDQUFDLE9BQXNDO0lBQ25FLElBQUksT0FBTyxLQUFLLFNBQVM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUN2QyxJQUFJLE9BQU8sT0FBTyxLQUFLLFFBQVE7UUFBRSxPQUFPLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDO0lBQ3BFLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3pDLE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1FBQzVCLElBQUksSUFBSSxFQUFFLElBQUksS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUMxQixPQUFPLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDO1FBQ3hFLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLDBCQUEwQixDQUFDLElBQVk7SUFDckQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QyxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3pCLE9BQU8sMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztBQUM3RSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5Qkc7QUFDSCxNQUFNLFVBQVUsMkJBQTJCLENBQUMsT0FBZ0M7SUFDMUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ2xELElBQUksT0FBTyxHQUFHLEtBQUssQ0FBQztJQUNwQixLQUFLLE1BQU0sTUFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNyQyxJQUFJLENBQUMsTUFBTSxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVE7WUFBRSxTQUFTO1FBQ3BELE1BQU0sT0FBTyxHQUFJLE1BQWdDLENBQUMsT0FBTyxDQUFDO1FBQzFELElBQUksQ0FBQyxPQUFPLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUTtZQUFFLFNBQVM7UUFDdEQsTUFBTSxNQUFNLEdBQUcsT0FBZ0MsQ0FBQztRQUNoRCxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDO1lBQUUsU0FBUztRQUM3QyxNQUFNLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ25GLE9BQU8sR0FBRyxJQUFJLENBQUM7SUFDakIsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJlcGFpcnMgdHJhZmZpYyBiZXR3ZWVuIE1hc3RyYSAvIHRoZSBBSSBTREsgYW5kIERhdGFicmlja3MgTW9kZWwgU2VydmluZydzXG4gKiBPcGVuQUktY29tcGF0aWJsZSBgL2NoYXQvY29tcGxldGlvbnNgIHJvdXRlLCBpbiBib3RoIGRpcmVjdGlvbnMuXG4gKlxuICogT3V0Ym91bmQgKHtAbGluayByZXdyaXRlU2VydmluZ0JvZHl9KSwgYmVjYXVzZSB0aGUgdHJhbnNjcmlwdCBNYXN0cmFcbiAqIHBlcnNpc3RzIGlzIG5vdCBhbHdheXMgYSB0cmFuc2NyaXB0IHRoZSBwcm92aWRlciB3aWxsIGFjY2VwdCBiYWNrOlxuICogRGF0YWJyaWNrcy1ob3N0ZWQgQ2xhdWRlIHJlamVjdHMgcmVwbGF5ZWQgZXh0ZW5kZWQtdGhpbmtpbmcgYmxvY2tzIGFuZCByZWFkc1xuICogYSB0cmFpbGluZyBhc3Npc3RhbnQgbWVzc2FnZSBhcyBhIHByZWZpbGwgcmVxdWVzdC5cbiAqXG4gKiBJbmJvdW5kICh7QGxpbmsgcmV3cml0ZVNlcnZpbmdSZXNwb25zZUJvZHl9KSwgYmVjYXVzZSBEYXRhYnJpY2tzLWhvc3RlZFxuICogR2VtaW5pIGFuc3dlcnMgd2l0aCBpdHMgbmF0aXZlIGNvbnRlbnQtcGFydHMgYXJyYXkgd2hlcmUgdGhlIE9wZW5BSSBjb250cmFjdFxuICogKGFuZCB0aGVyZWZvcmUgdGhlIEFJIFNESydzIHJlc3BvbnNlIHNjaGVtYSkgcmVxdWlyZXMgYSBwbGFpbiBzdHJpbmcuXG4gKlxuICogRXZlcnkgcmVwYWlyIGhlcmUgaXMgYSBwcm92aWRlciBxdWlyayByYXRoZXIgdGhhbiBhIHNjaGVtYSB2aW9sYXRpb24sIHNvIGFsbFxuICogb2YgdGhlbSBhcmUgYXBwbGllZCBvbiB0aGUgd2lyZSAoc2VlIHRoZSBgZ2xvYmFsVGhpcy5mZXRjaGAgd3JhcHBlciBpblxuICogYG1vZGVsLnRzYCkgcmF0aGVyIHRoYW4gYnkgY2hhbmdpbmcgd2hhdCB0aGUgYWdlbnQgc3RvcmVzIG9yIHdoYXQgdGhlIFVJXG4gKiBzaG93cy5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsganNvbiwgc3RyaW5nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7XG4gIHR5cGUgQ2hhdE1lc3NhZ2UsXG4gIHR5cGUgQ2hhdFJvbGUsXG4gIG9wZW5haUNoYXQsXG4gIG9wZW5haVJlc3BvbnNlcyxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsXCI7XG5cbi8qKlxuICogQSBjaGF0IG1lc3NhZ2UgYXMgaXQgYXJyaXZlcyBvbiB0aGUgc2VydmluZyB3aXJlLCBwbHVzIHRoZSBleHRlbmRlZC10aGlua2luZ1xuICogZmllbGRzIERhdGFicmlja3MtaG9zdGVkIENsYXVkZSBhZGRzLiBUaGUgT3BlbkFJLXN0YW5kYXJkIHBhcnQgb2YgdGhlIHNoYXBlXG4gKiBpcyB7QGxpbmsgQ2hhdE1lc3NhZ2V9IGZyb20gYEBkYngtdG9vbHMvc2hhcmVkLW1vZGVsYDsgb25seSB0aGUgcHJvdmlkZXJcbiAqIGV4dGVuc2lvbnMgdGhpcyBtb2R1bGUgc3RyaXBzIGFyZSBkZWNsYXJlZCBoZXJlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZpbmdDaGF0TWVzc2FnZSBleHRlbmRzIENoYXRNZXNzYWdlIHtcbiAgLyoqIE5hcnJvd2VkIHRvIHRoZSByb2xlcyB0aGlzIHJlcGFpciBwYXNzIHJlYXNvbnMgYWJvdXQsIHBsdXMgQ2xhdWRlJ3MgYHJlYXNvbmluZ2AgdHVybi4gKi9cbiAgcm9sZTogQ2hhdFJvbGUgfCBcInJlYXNvbmluZ1wiO1xuICByZWFzb25pbmc/OiB1bmtub3duO1xuICByZWFzb25pbmdfY29udGVudD86IHVua25vd247XG59XG5cbi8vIFNoYXJlZCB3aXRoIHRoZSBSZXNwb25zZXMgc2FuaXRpemUgcGF0aCBzbyBib3RoIHdpcmUgc3VyZmFjZXMgYWdyZWUgb24gd2hhdFxuLy8gY291bnRzIGFzIGEgc2lnbmVkIHJlYXNvbmluZyBibG9jay5cbmNvbnN0IFJFQVNPTklOR19QQVJUX1RZUEVTID0gb3BlbmFpUmVzcG9uc2VzLlJFQVNPTklOR19UWVBFUztcblxuLyoqXG4gKiBQYXJzZSwgc2FuaXRpemUsIGFuZCByZS1zZXJpYWxpemUgYSBgL3NlcnZpbmctZW5kcG9pbnRzLy4uLmAgUE9TVFxuICogYm9keS4gUmV0dXJucyB0aGUgb3JpZ2luYWwgc3RyaW5nIHZlcmJhdGltIHdoZW4gdGhlIGJvZHkgaXMgbm90XG4gKiBKU09OIG9yIG5vIHJld3JpdGUgd2FzIG5lZWRlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJld3JpdGVTZXJ2aW5nQm9keShib2R5OiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBwYXJzZWQgPSBqc29uLnBhcnNlUmVjb3JkKGJvZHkpO1xuICBpZiAoIXBhcnNlZCkgcmV0dXJuIGJvZHk7XG5cbiAgLy8gUnVucyByZWdhcmRsZXNzIG9mIGBtZXNzYWdlc2A6IERhdGFicmlja3MgcmVmdXNlcyB0byBwYXJzZSBhIGJvZHkgY2FycnlpbmdcbiAgLy8gYW4gdW5rbm93biB0b3AtbGV2ZWwgZmllbGQsIHNvIHRoaXMgZmFpbHVyZSBpcyBub3Qgc3BlY2lmaWMgdG8gYSB0cmFuc2NyaXB0LlxuICBsZXQgY2hhbmdlZCA9IG9wZW5haUNoYXQuc3RyaXBVbnN1cHBvcnRlZENoYXRGaWVsZHMocGFyc2VkKS5sZW5ndGggPiAwO1xuXG4gIGlmIChBcnJheS5pc0FycmF5KHBhcnNlZC5tZXNzYWdlcykpIHtcbiAgICBjb25zdCBtZXNzYWdlcyA9IHBhcnNlZC5tZXNzYWdlcyBhcyBTZXJ2aW5nQ2hhdE1lc3NhZ2VbXTtcbiAgICAvLyBFdmFsdWF0ZWQgZWFnZXJseSwgbm90IHNob3J0LWNpcmN1aXRlZDogb25lIHRyYW5zY3JpcHQgY2FuIG5lZWQgYm90aFxuICAgIC8vIHJlYXNvbmluZyBibG9ja3Mgc3RyaXBwZWQgQU5EIGEgdHJhaWxpbmcgYXNzaXN0YW50IHByZWZpbGwgZm9sZGVkIGJhY2suXG4gICAgY29uc3Qgc3RyaXBwZWQgPSBzdHJpcFJlYXNvbmluZ0Zyb21TZXJ2aW5nTWVzc2FnZXMobWVzc2FnZXMpO1xuICAgIGNvbnN0IHJlcGFpcmVkID0gcmVwYWlyQXNzaXN0YW50UHJlZmlsbChtZXNzYWdlcyk7XG4gICAgY2hhbmdlZCA9IGNoYW5nZWQgfHwgc3RyaXBwZWQgfHwgcmVwYWlyZWQ7XG4gIH1cblxuICByZXR1cm4gY2hhbmdlZCA/IEpTT04uc3RyaW5naWZ5KHBhcnNlZCkgOiBib2R5O1xufVxuXG4vKipcbiAqIERyb3AgZXh0ZW5kZWQtdGhpbmtpbmcgLyByZWFzb25pbmcgYmxvY2tzIGZyb20gYSByZXBsYXllZCB0cmFuc2NyaXB0LlxuICpcbiAqIEh5YnJpZCBDbGF1ZGUgZW5kcG9pbnRzIChlLmcuIFNvbm5ldCA0LjUrKSBtYXkgZW1pdCBgcmVhc29uaW5nYCBjb250ZW50XG4gKiBwYXJ0cyBvbiB0aGUgZmlyc3QgdHVybi4gTWFzdHJhIHBlcnNpc3RzIGFuZCByZXBsYXlzIHRoZW0gb24gdGhlIG5leHRcbiAqIGFnZW50IHN0ZXAsIGJ1dCBEYXRhYnJpY2tzLWhvc3RlZCBDbGF1ZGUgcmVqZWN0cyB0aG9zZSBibG9ja3Mgb25cbiAqIG11bHRpLXR1cm4gdG9vbCBjb250aW51YXRpb25zLiBUaGUgVUkgYWxyZWFkeSBjYXB0dXJlZCByZWFzb25pbmcgZm9yXG4gKiBkaXNwbGF5OyBzdHJpcHBpbmcgaGVyZSBrZWVwcyBwcm92aWRlciByZXBsYXkgY29tcGF0aWJsZSB3aXRob3V0XG4gKiBjaGFuZ2luZyB3aGF0IHVzZXJzIHNlZSBpbiB0aGUgY2hhdCBidWJibGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdHJpcFJlYXNvbmluZ0Zyb21TZXJ2aW5nTWVzc2FnZXMobWVzc2FnZXM6IFNlcnZpbmdDaGF0TWVzc2FnZVtdKTogYm9vbGVhbiB7XG4gIGxldCBjaGFuZ2VkID0gZmFsc2U7XG4gIGZvciAobGV0IGkgPSBtZXNzYWdlcy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgIGNvbnN0IG1zZyA9IG1lc3NhZ2VzW2ldO1xuICAgIGlmICghbXNnKSBjb250aW51ZTtcbiAgICBpZiAobXNnLnJvbGUgPT09IFwicmVhc29uaW5nXCIpIHtcbiAgICAgIG1lc3NhZ2VzLnNwbGljZShpLCAxKTtcbiAgICAgIGNoYW5nZWQgPSB0cnVlO1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGlmIChtc2cucmVhc29uaW5nICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGRlbGV0ZSBtc2cucmVhc29uaW5nO1xuICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgfVxuICAgIGlmIChtc2cucmVhc29uaW5nX2NvbnRlbnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgZGVsZXRlIG1zZy5yZWFzb25pbmdfY29udGVudDtcbiAgICAgIGNoYW5nZWQgPSB0cnVlO1xuICAgIH1cbiAgICBpZiAoIUFycmF5LmlzQXJyYXkobXNnLmNvbnRlbnQpKSBjb250aW51ZTtcbiAgICBjb25zdCBmaWx0ZXJlZCA9IG1zZy5jb250ZW50LmZpbHRlcigocGFydCkgPT4ge1xuICAgICAgY29uc3QgdHlwZSA9IHBhcnQ/LnR5cGU7XG4gICAgICBpZiAodHlwZW9mIHR5cGUgPT09IFwic3RyaW5nXCIgJiYgUkVBU09OSU5HX1BBUlRfVFlQRVMuaGFzKHR5cGUpKSB7XG4gICAgICAgIGNoYW5nZWQgPSB0cnVlO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgICBpZiAoZmlsdGVyZWQubGVuZ3RoICE9PSBtc2cuY29udGVudC5sZW5ndGgpIHtcbiAgICAgIG1zZy5jb250ZW50ID0gZmlsdGVyZWQ7XG4gICAgfVxuICAgIGNvbnN0IGhhc1Rvb2xDYWxscyA9IEFycmF5LmlzQXJyYXkobXNnLnRvb2xfY2FsbHMpICYmIG1zZy50b29sX2NhbGxzLmxlbmd0aCA+IDA7XG4gICAgaWYgKG1zZy5yb2xlID09PSBcImFzc2lzdGFudFwiICYmICFoYXNUb29sQ2FsbHMgJiYgaXNFbXB0eVNlcnZpbmdDb250ZW50KG1zZy5jb250ZW50KSkge1xuICAgICAgbWVzc2FnZXMuc3BsaWNlKGksIDEpO1xuICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBjaGFuZ2VkO1xufVxuXG4vKipcbiAqIFJlcGFpciBhIE1hc3RyYS9BSSBTREsgbWVzc2FnZSByZXBsYXkgdGhhdCBEYXRhYnJpY2tzLWhvc3RlZCBDbGF1ZGVcbiAqIHJlamVjdHMgd2l0aCBgXCJUaGlzIG1vZGVsIGRvZXMgbm90IHN1cHBvcnQgYXNzaXN0YW50IG1lc3NhZ2VcbiAqIHByZWZpbGwuIFRoZSBjb252ZXJzYXRpb24gbXVzdCBlbmQgd2l0aCBhIHVzZXIgbWVzc2FnZS5cImAuXG4gKlxuICogVGhlIGJ1ZyBwYXR0ZXJuOiB3aGVuIGFuIGFzc2lzdGFudCB0dXJuIHN0cmVhbXMgdGV4dCAqYW5kKiBhXG4gKiBgdG9vbF9jYWxsYCwgdGhlIEFJIFNESyBwZXJzaXN0cyB0aGVtIGFzIHR3byBzZXBhcmF0ZSBhc3Npc3RhbnRcbiAqIGVudHJpZXMgKHRleHQtb25seSBhbmQgdG9vbC1jYWxsLW9ubHkpLiBPbiB0aGUgbmV4dCBhZ2VudCBzdGVwIHRoZVxuICogdG9vbC1jYWxsIGVudHJ5IGlzIHJlcGxheWVkICpiZWZvcmUqIHRoZSB0b29sIHJlc3VsdCBhbmQgdGhlXG4gKiB0ZXh0IGVudHJ5IGlzIHJlcGxheWVkICphZnRlciogaXQsIHNvIHRoZSBjb252ZXJzYXRpb24gZW5kcyB3aXRoIGFcbiAqIHRyYWlsaW5nIGFzc2lzdGFudCB0ZXh0IG1lc3NhZ2UuIEFudGhyb3BpYyBpbnRlcnByZXRzIHRoYXQgYXMgYVxuICogcHJlZmlsbCByZXF1ZXN0IGFuZCByZWplY3RzIGl0IG9uIERhdGFicmlja3MgKHRoZSB1cHN0cmVhbSBCZWRyb2NrXG4gKiByb3V0ZSBkaXNhbGxvd3MgcHJlZmlsbCkuXG4gKlxuICogRml4OiB3aGVuIHRoZSBsYXN0IG1lc3NhZ2UgaXMgYW4gYXNzaXN0YW50IHRleHQgd2l0aCBubyBgdG9vbF9jYWxsc2BcbiAqIGFuZCB0aGUgY2hhaW4gaW1tZWRpYXRlbHkgYmVmb3JlIGl0IGlzIGBhc3Npc3RhbnQodG9vbF9jYWxscz0uLi4pYFxuICogZm9sbG93ZWQgb25seSBieSBgdG9vbCguLi4pYCByZXN1bHRzLCBmb2xkIHRoZSB0cmFpbGluZyB0ZXh0IGJhY2tcbiAqIGludG8gdGhlIGBjb250ZW50YCBvZiB0aGF0IG9wZW5pbmcgYXNzaXN0YW50IGFuZCBkcm9wIHRoZSBkdXBsaWNhdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXBhaXJBc3Npc3RhbnRQcmVmaWxsKG1lc3NhZ2VzOiBTZXJ2aW5nQ2hhdE1lc3NhZ2VbXSk6IGJvb2xlYW4ge1xuICBpZiAobWVzc2FnZXMubGVuZ3RoIDwgMikgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBsYXN0ID0gbWVzc2FnZXNbbWVzc2FnZXMubGVuZ3RoIC0gMV07XG4gIGlmICghbGFzdCB8fCBsYXN0LnJvbGUgIT09IFwiYXNzaXN0YW50XCIgfHwgKGxhc3QudG9vbF9jYWxscyAmJiBsYXN0LnRvb2xfY2FsbHMubGVuZ3RoID4gMCkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBsZXQgaSA9IG1lc3NhZ2VzLmxlbmd0aCAtIDI7XG4gIHdoaWxlIChpID49IDAgJiYgbWVzc2FnZXNbaV0/LnJvbGUgPT09IFwidG9vbFwiKSBpLS07XG4gIGlmIChpIDwgMCkgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBvcGVuZXIgPSBtZXNzYWdlc1tpXTtcbiAgaWYgKFxuICAgICFvcGVuZXIgfHxcbiAgICBvcGVuZXIucm9sZSAhPT0gXCJhc3Npc3RhbnRcIiB8fFxuICAgICFvcGVuZXIudG9vbF9jYWxscyB8fFxuICAgIG9wZW5lci50b29sX2NhbGxzLmxlbmd0aCA9PT0gMFxuICApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBjb25zdCBtZXJnZWQgPSBbXG4gICAgc3RyaW5nLnRyaW1Ub051bGwodGV4dEZyb21TZXJ2aW5nQ29udGVudChvcGVuZXIuY29udGVudCkpLFxuICAgIHN0cmluZy50cmltVG9OdWxsKHRleHRGcm9tU2VydmluZ0NvbnRlbnQobGFzdC5jb250ZW50KSksXG4gIF1cbiAgICAuZmlsdGVyKChzKTogcyBpcyBzdHJpbmcgPT4gcyAhPT0gbnVsbClcbiAgICAuam9pbihcIlxcblxcblwiKTtcbiAgb3BlbmVyLmNvbnRlbnQgPSBtZXJnZWQ7XG4gIG1lc3NhZ2VzLnBvcCgpO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqXG4gKiBGbGF0dGVuIGNvbnRlbnQgdG8gdGhlIHByb3NlIGEgaHVtYW4gd291bGQgcmVhZDogdGV4dCBwYXJ0cyBvbmx5LCBzZXBhcmF0ZWRcbiAqIGJ5IGJsYW5rIGxpbmVzLCBzaW5jZSB0aGUgcmVzdWx0IGlzIHNwbGljZWQgaW50byBhbm90aGVyIHR1cm4ncyBtZXNzYWdlIGJvZHkuXG4gKi9cbmZ1bmN0aW9uIHRleHRGcm9tU2VydmluZ0NvbnRlbnQoY29udGVudDogU2VydmluZ0NoYXRNZXNzYWdlW1wiY29udGVudFwiXSk6IHN0cmluZyB7XG4gIHJldHVybiBvcGVuYWlDaGF0LmNoYXRDb250ZW50VG9UZXh0KGNvbnRlbnQsIHsgc2VwYXJhdG9yOiBcIlxcblxcblwiLCB0eXBlczogW1widGV4dFwiXSB9KTtcbn1cblxuLyoqXG4gKiBXaGV0aGVyIGEgdHVybiBjYXJyaWVzIG5vdGhpbmcgd29ydGggcmVwbGF5aW5nLiBEZWxpYmVyYXRlbHkgc3RyaWN0ZXIgdGhhblxuICogXCJoYXMgbm8gdGV4dFwiOiBhIHBhcnQgb2YgYW55IG90aGVyIHR5cGUgKGFuIGltYWdlLCBhIGNhY2hlIG1hcmtlcikgY291bnRzIGFzXG4gKiBjb250ZW50LCBzbyBhIG1lc3NhZ2UgaG9sZGluZyBvbmUgaXMga2VwdCBldmVuIHRob3VnaCBpdCBoYXMgbm8gcHJvc2UuXG4gKi9cbmZ1bmN0aW9uIGlzRW1wdHlTZXJ2aW5nQ29udGVudChjb250ZW50OiBTZXJ2aW5nQ2hhdE1lc3NhZ2VbXCJjb250ZW50XCJdKTogYm9vbGVhbiB7XG4gIGlmIChjb250ZW50ID09PSB1bmRlZmluZWQpIHJldHVybiB0cnVlO1xuICBpZiAodHlwZW9mIGNvbnRlbnQgPT09IFwic3RyaW5nXCIpIHJldHVybiBjb250ZW50LnRyaW0oKS5sZW5ndGggPT09IDA7XG4gIGlmICghQXJyYXkuaXNBcnJheShjb250ZW50KSkgcmV0dXJuIHRydWU7XG4gIHJldHVybiBjb250ZW50LmV2ZXJ5KChwYXJ0KSA9PiB7XG4gICAgaWYgKHBhcnQ/LnR5cGUgPT09IFwidGV4dFwiKSB7XG4gICAgICByZXR1cm4gdHlwZW9mIHBhcnQudGV4dCAhPT0gXCJzdHJpbmdcIiB8fCBwYXJ0LnRleHQudHJpbSgpLmxlbmd0aCA9PT0gMDtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9KTtcbn1cblxuLyoqXG4gKiBQYXJzZSwgc2FuaXRpemUsIGFuZCByZS1zZXJpYWxpemUgYSBgL3NlcnZpbmctZW5kcG9pbnRzLy4uLmAgbm9uLXN0cmVhbWluZ1xuICogSlNPTiBSRVNQT05TRSBib2R5LiBSZXR1cm5zIHRoZSBvcmlnaW5hbCBzdHJpbmcgdmVyYmF0aW0gd2hlbiB0aGUgYm9keSBpc1xuICogbm90IEpTT04gb3Igbm8gcmV3cml0ZSB3YXMgbmVlZGVkLCBtaXJyb3JpbmdcbiAqIHtAbGluayByZXdyaXRlU2VydmluZ0JvZHl9IG9uIHRoZSByZXF1ZXN0IHNpZGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXdyaXRlU2VydmluZ1Jlc3BvbnNlQm9keShib2R5OiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBwYXJzZWQgPSBqc29uLnBhcnNlUmVjb3JkKGJvZHkpO1xuICBpZiAoIXBhcnNlZCkgcmV0dXJuIGJvZHk7XG4gIHJldHVybiBmbGF0dGVuQ2hvaWNlTWVzc2FnZUNvbnRlbnQocGFyc2VkKSA/IEpTT04uc3RyaW5naWZ5KHBhcnNlZCkgOiBib2R5O1xufVxuXG4vKipcbiAqIENvbGxhcHNlIGEgc3RydWN0dXJlZCBgY2hvaWNlc1tdLm1lc3NhZ2UuY29udGVudGAgYXJyYXkgdG8gdGhlIHBsYWluIHN0cmluZ1xuICogdGhlIE9wZW5BSSBDaGF0IENvbXBsZXRpb25zIGNvbnRyYWN0IHNwZWNpZmllcy5cbiAqXG4gKiBEYXRhYnJpY2tzLWhvc3RlZCBHZW1pbmkgYW5zd2VycyBhIG5vbi1zdHJlYW1pbmcgYC9jaGF0L2NvbXBsZXRpb25zYCBjYWxsXG4gKiB3aXRoIHRoZSBHZW1pbmktbmF0aXZlIHBhcnRzIHNoYXBlOlxuICpcbiAqIGBgYGpzb25cbiAqIFwiY29udGVudFwiOiBbeyBcInR5cGVcIjogXCJ0ZXh0XCIsIFwidGV4dFwiOiBcIi4uLlwiLCBcInRob3VnaHRTaWduYXR1cmVcIjogXCIuLi5cIiB9XVxuICogYGBgXG4gKlxuICogYEBhaS1zZGsvb3BlbmFpLWNvbXBhdGlibGVgIHZhbGlkYXRlcyB0aGUgcmVzcG9uc2UgYWdhaW5zdCB0aGUgT3BlbkFJXG4gKiBzY2hlbWEsIHdoZXJlIGBjb250ZW50YCBpcyBhIG51bGxhYmxlIHN0cmluZywgc28gaXQgdGhyb3dzXG4gKiBgVHlwZSB2YWxpZGF0aW9uIGZhaWxlZDogLi4uIGV4cGVjdGVkIHN0cmluZywgcmVjZWl2ZWQgYXJyYXlgIGFuZCB0aGUgd2hvbGVcbiAqIGNhbGwgZmFpbHMgKGBBSV9BUElDYWxsRXJyb3I6IEludmFsaWQgSlNPTiByZXNwb25zZWAgb24gYW4gSFRUUCAyMDApLiBNYXN0cmFcbiAqIHVzZXMgYGRvR2VuZXJhdGVgIGZvciBpdHMgc2lkZSBjYWxscywgc28gdGhlIHZpc2libGUgc3ltcHRvbSBpc1xuICogYEVycm9yIGdlbmVyYXRpbmcgdGl0bGVgIC0gZXZlcnkgdGhyZWFkIGtlZXBzIGl0cyBwbGFjZWhvbGRlciBuYW1lLlxuICpcbiAqIEZsYXR0ZW5pbmcgb24gdGhlIHdpcmUga2VlcHMgdGhlIHJlcGFpciBpbiBvbmUgcGxhY2U6IHRoZSBzdHJlYW1pbmcgcGF0aCBpc1xuICogdW5hZmZlY3RlZCAoZGVsdGFzIGFscmVhZHkgY2Fycnkgc3RyaW5nIGNvbnRlbnQpLCBhbmQgbmVpdGhlciB0aGUgYWdlbnQnc1xuICogc3RvcmVkIHRyYW5zY3JpcHQgbm9yIHRoZSBVSSBoYXMgdG8ga25vdyB0aGUgcHJvdmlkZXIgZW1pdHRlZCBwYXJ0cy4gQW55XG4gKiBub24tdGV4dCBwYXJ0IChhIGB0aG91Z2h0U2lnbmF0dXJlYC1vbmx5IGVudHJ5LCBhbiBpbmxpbmUgaW1hZ2UpIGNvbnRyaWJ1dGVzXG4gKiBub3RoaW5nLCBtYXRjaGluZyB7QGxpbmsgb3BlbmFpQ2hhdC5jaGF0Q29udGVudFRvVGV4dH0sIGFuZCBhbiBhbGwtcGFydHMtZW1wdHlcbiAqIG1lc3NhZ2UgZmxhdHRlbnMgdG8gYFwiXCJgIHJhdGhlciB0aGFuIGJlaW5nIGRyb3BwZWQsIHNvIGBmaW5pc2hfcmVhc29uYCBhbmRcbiAqIGB1c2FnZWAgc3RpbGwgcm91bmQtdHJpcC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZsYXR0ZW5DaG9pY2VNZXNzYWdlQ29udGVudChwYXlsb2FkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IGJvb2xlYW4ge1xuICBpZiAoIUFycmF5LmlzQXJyYXkocGF5bG9hZC5jaG9pY2VzKSkgcmV0dXJuIGZhbHNlO1xuICBsZXQgY2hhbmdlZCA9IGZhbHNlO1xuICBmb3IgKGNvbnN0IGNob2ljZSBvZiBwYXlsb2FkLmNob2ljZXMpIHtcbiAgICBpZiAoIWNob2ljZSB8fCB0eXBlb2YgY2hvaWNlICE9PSBcIm9iamVjdFwiKSBjb250aW51ZTtcbiAgICBjb25zdCBtZXNzYWdlID0gKGNob2ljZSBhcyB7IG1lc3NhZ2U/OiB1bmtub3duIH0pLm1lc3NhZ2U7XG4gICAgaWYgKCFtZXNzYWdlIHx8IHR5cGVvZiBtZXNzYWdlICE9PSBcIm9iamVjdFwiKSBjb250aW51ZTtcbiAgICBjb25zdCB0YXJnZXQgPSBtZXNzYWdlIGFzIHsgY29udGVudD86IHVua25vd24gfTtcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkodGFyZ2V0LmNvbnRlbnQpKSBjb250aW51ZTtcbiAgICB0YXJnZXQuY29udGVudCA9IG9wZW5haUNoYXQuY2hhdENvbnRlbnRUb1RleHQodGFyZ2V0LmNvbnRlbnQsIHsgdHlwZXM6IFtcInRleHRcIl0gfSk7XG4gICAgY2hhbmdlZCA9IHRydWU7XG4gIH1cbiAgcmV0dXJuIGNoYW5nZWQ7XG59XG4iXX0=
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra-specific glue over the generic `@dbx-tools/model` toolkit.
|
|
3
|
+
*
|
|
4
|
+
* The live `/serving-endpoints` catalogue access, fuzzy name
|
|
5
|
+
* resolution, and class/fallback selection all live in
|
|
6
|
+
* `@dbx-tools/model`; this module only adds what is specific to the
|
|
7
|
+
* Mastra plugin: pulling a per-request model override off an HTTP
|
|
8
|
+
* request (header / query / body) and projecting the plugin config
|
|
9
|
+
* onto the knobs `buildModel` and the `/models` route share.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { MastraPluginConfig } from "./config.js";
|
|
14
|
+
/**
|
|
15
|
+
* `RequestContext` key under which {@link MastraServer} stores the
|
|
16
|
+
* per-request model override (header / query / body). `model.ts`
|
|
17
|
+
* reads it before falling back to the agent / plugin default.
|
|
18
|
+
*/
|
|
19
|
+
export declare const MASTRA_MODEL_OVERRIDE_KEY = "mastra__model_override";
|
|
20
|
+
/**
|
|
21
|
+
* Minimal Express-ish request shape used by {@link extractModelOverride}.
|
|
22
|
+
* Keeps this module independent of `express` so the helper can be
|
|
23
|
+
* reused from non-Express adapters.
|
|
24
|
+
*/
|
|
25
|
+
export interface ModelOverrideRequest {
|
|
26
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
27
|
+
query?: Record<string, unknown> | undefined;
|
|
28
|
+
body?: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Pull a model override out of a single HTTP request, checking
|
|
32
|
+
* sources in priority order:
|
|
33
|
+
*
|
|
34
|
+
* 1. `X-Mastra-Model` header
|
|
35
|
+
* 2. `?model=` query string parameter
|
|
36
|
+
* 3. Body field (`model` or `modelId`, in that order)
|
|
37
|
+
*
|
|
38
|
+
* Returns `null` when nothing is set, so callers can wrap with
|
|
39
|
+
* `if (override) ...` without juggling empty strings. Body inspection
|
|
40
|
+
* is lenient - any plain object with one of the configured keys
|
|
41
|
+
* counts, mirroring how AI SDK chat clients pass arbitrary metadata
|
|
42
|
+
* alongside `messages`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractModelOverride(req: ModelOverrideRequest): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Read the fuzzy-resolution config knobs off the plugin config with
|
|
47
|
+
* `@dbx-tools/model` defaults applied. Kept here so `buildModel` and
|
|
48
|
+
* the `/models` route agree on what "enabled" means.
|
|
49
|
+
*
|
|
50
|
+
* `fallbacks` is the priority-ordered list `resolveModel` walks first
|
|
51
|
+
* when nothing explicit is set; defaults to an empty list so the
|
|
52
|
+
* generic resolver falls through to the live catalogue and its own
|
|
53
|
+
* `FALLBACK_MODEL_IDS` floor.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveServingConfig(config: MastraPluginConfig): {
|
|
56
|
+
ttlMs: number;
|
|
57
|
+
threshold: number;
|
|
58
|
+
fuzzy: boolean;
|
|
59
|
+
allowOverride: boolean;
|
|
60
|
+
fallbacks: readonly string[];
|
|
61
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra-specific glue over the generic `@dbx-tools/model` toolkit.
|
|
3
|
+
*
|
|
4
|
+
* The live `/serving-endpoints` catalogue access, fuzzy name
|
|
5
|
+
* resolution, and class/fallback selection all live in
|
|
6
|
+
* `@dbx-tools/model`; this module only adds what is specific to the
|
|
7
|
+
* Mastra plugin: pulling a per-request model override off an HTTP
|
|
8
|
+
* request (header / query / body) and projecting the plugin config
|
|
9
|
+
* onto the knobs `buildModel` and the `/models` route share.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { serving as nodeServing } from "@dbx-tools/model";
|
|
14
|
+
import { string } from "@dbx-tools/shared-core";
|
|
15
|
+
import { override } from "@dbx-tools/shared-mastra";
|
|
16
|
+
/**
|
|
17
|
+
* `RequestContext` key under which {@link MastraServer} stores the
|
|
18
|
+
* per-request model override (header / query / body). `model.ts`
|
|
19
|
+
* reads it before falling back to the agent / plugin default.
|
|
20
|
+
*/
|
|
21
|
+
export const MASTRA_MODEL_OVERRIDE_KEY = "mastra__model_override";
|
|
22
|
+
/**
|
|
23
|
+
* Pull a model override out of a single HTTP request, checking
|
|
24
|
+
* sources in priority order:
|
|
25
|
+
*
|
|
26
|
+
* 1. `X-Mastra-Model` header
|
|
27
|
+
* 2. `?model=` query string parameter
|
|
28
|
+
* 3. Body field (`model` or `modelId`, in that order)
|
|
29
|
+
*
|
|
30
|
+
* Returns `null` when nothing is set, so callers can wrap with
|
|
31
|
+
* `if (override) ...` without juggling empty strings. Body inspection
|
|
32
|
+
* is lenient - any plain object with one of the configured keys
|
|
33
|
+
* counts, mirroring how AI SDK chat clients pass arbitrary metadata
|
|
34
|
+
* alongside `messages`.
|
|
35
|
+
*/
|
|
36
|
+
export function extractModelOverride(req) {
|
|
37
|
+
const headers = req.headers;
|
|
38
|
+
if (headers) {
|
|
39
|
+
const headerVal = string.firstNonEmpty(headers[override.MODEL_OVERRIDE_HEADER] ??
|
|
40
|
+
headers[override.MODEL_OVERRIDE_HEADER.toLowerCase()]);
|
|
41
|
+
if (headerVal)
|
|
42
|
+
return headerVal;
|
|
43
|
+
}
|
|
44
|
+
if (req.query) {
|
|
45
|
+
const queryVal = string.firstNonEmpty(req.query[override.MODEL_OVERRIDE_QUERY]);
|
|
46
|
+
if (queryVal)
|
|
47
|
+
return queryVal;
|
|
48
|
+
}
|
|
49
|
+
if (req.body && typeof req.body === "object") {
|
|
50
|
+
const record = req.body;
|
|
51
|
+
for (const field of override.MODEL_OVERRIDE_BODY_FIELDS) {
|
|
52
|
+
const bodyVal = string.firstNonEmpty(record[field]);
|
|
53
|
+
if (bodyVal)
|
|
54
|
+
return bodyVal;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Read the fuzzy-resolution config knobs off the plugin config with
|
|
61
|
+
* `@dbx-tools/model` defaults applied. Kept here so `buildModel` and
|
|
62
|
+
* the `/models` route agree on what "enabled" means.
|
|
63
|
+
*
|
|
64
|
+
* `fallbacks` is the priority-ordered list `resolveModel` walks first
|
|
65
|
+
* when nothing explicit is set; defaults to an empty list so the
|
|
66
|
+
* generic resolver falls through to the live catalogue and its own
|
|
67
|
+
* `FALLBACK_MODEL_IDS` floor.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveServingConfig(config) {
|
|
70
|
+
return {
|
|
71
|
+
ttlMs: config.modelCacheTtlMs ?? nodeServing.DEFAULT_MODEL_CACHE_TTL_MS,
|
|
72
|
+
threshold: config.modelFuzzyThreshold ?? nodeServing.DEFAULT_FUZZY_THRESHOLD,
|
|
73
|
+
fuzzy: config.modelFuzzyMatch !== false,
|
|
74
|
+
allowOverride: config.modelOverride !== false,
|
|
75
|
+
fallbacks: config.defaultModelFallbacks ?? [],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7OztHQVdHO0FBRUgsT0FBTyxFQUFFLE9BQU8sSUFBSSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBSXBEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyx3QkFBd0IsQ0FBQztBQWFsRTs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLEdBQXlCO0lBQzVELE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUM7SUFDNUIsSUFBSSxPQUFPLEVBQUUsQ0FBQztRQUNaLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQ3BDLE9BQU8sQ0FBQyxRQUFRLENBQUMscUJBQXFCLENBQUM7WUFDckMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUN4RCxDQUFDO1FBQ0YsSUFBSSxTQUFTO1lBQUUsT0FBTyxTQUFTLENBQUM7SUFDbEMsQ0FBQztJQUNELElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2QsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUM7UUFDaEYsSUFBSSxRQUFRO1lBQUUsT0FBTyxRQUFRLENBQUM7SUFDaEMsQ0FBQztJQUNELElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxPQUFPLEdBQUcsQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDN0MsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLElBQStCLENBQUM7UUFDbkQsS0FBSyxNQUFNLEtBQUssSUFBSSxRQUFRLENBQUMsMEJBQTBCLEVBQUUsQ0FBQztZQUN4RCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1lBQ3BELElBQUksT0FBTztnQkFBRSxPQUFPLE9BQU8sQ0FBQztRQUM5QixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sVUFBVSxvQkFBb0IsQ0FBQyxNQUEwQjtJQU83RCxPQUFPO1FBQ0wsS0FBSyxFQUFFLE1BQU0sQ0FBQyxlQUFlLElBQUksV0FBVyxDQUFDLDBCQUEwQjtRQUN2RSxTQUFTLEVBQUUsTUFBTSxDQUFDLG1CQUFtQixJQUFJLFdBQVcsQ0FBQyx1QkFBdUI7UUFDNUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxlQUFlLEtBQUssS0FBSztRQUN2QyxhQUFhLEVBQUUsTUFBTSxDQUFDLGFBQWEsS0FBSyxLQUFLO1FBQzdDLFNBQVMsRUFBRSxNQUFNLENBQUMscUJBQXFCLElBQUksRUFBRTtLQUM5QyxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTWFzdHJhLXNwZWNpZmljIGdsdWUgb3ZlciB0aGUgZ2VuZXJpYyBgQGRieC10b29scy9tb2RlbGAgdG9vbGtpdC5cbiAqXG4gKiBUaGUgbGl2ZSBgL3NlcnZpbmctZW5kcG9pbnRzYCBjYXRhbG9ndWUgYWNjZXNzLCBmdXp6eSBuYW1lXG4gKiByZXNvbHV0aW9uLCBhbmQgY2xhc3MvZmFsbGJhY2sgc2VsZWN0aW9uIGFsbCBsaXZlIGluXG4gKiBgQGRieC10b29scy9tb2RlbGA7IHRoaXMgbW9kdWxlIG9ubHkgYWRkcyB3aGF0IGlzIHNwZWNpZmljIHRvIHRoZVxuICogTWFzdHJhIHBsdWdpbjogcHVsbGluZyBhIHBlci1yZXF1ZXN0IG1vZGVsIG92ZXJyaWRlIG9mZiBhbiBIVFRQXG4gKiByZXF1ZXN0IChoZWFkZXIgLyBxdWVyeSAvIGJvZHkpIGFuZCBwcm9qZWN0aW5nIHRoZSBwbHVnaW4gY29uZmlnXG4gKiBvbnRvIHRoZSBrbm9icyBgYnVpbGRNb2RlbGAgYW5kIHRoZSBgL21vZGVsc2Agcm91dGUgc2hhcmUuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHNlcnZpbmcgYXMgbm9kZVNlcnZpbmcgfSBmcm9tIFwiQGRieC10b29scy9tb2RlbFwiO1xuaW1wb3J0IHsgc3RyaW5nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IG92ZXJyaWRlIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1hc3RyYVwiO1xuXG5pbXBvcnQgdHlwZSB7IE1hc3RyYVBsdWdpbkNvbmZpZyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuXG4vKipcbiAqIGBSZXF1ZXN0Q29udGV4dGAga2V5IHVuZGVyIHdoaWNoIHtAbGluayBNYXN0cmFTZXJ2ZXJ9IHN0b3JlcyB0aGVcbiAqIHBlci1yZXF1ZXN0IG1vZGVsIG92ZXJyaWRlIChoZWFkZXIgLyBxdWVyeSAvIGJvZHkpLiBgbW9kZWwudHNgXG4gKiByZWFkcyBpdCBiZWZvcmUgZmFsbGluZyBiYWNrIHRvIHRoZSBhZ2VudCAvIHBsdWdpbiBkZWZhdWx0LlxuICovXG5leHBvcnQgY29uc3QgTUFTVFJBX01PREVMX09WRVJSSURFX0tFWSA9IFwibWFzdHJhX19tb2RlbF9vdmVycmlkZVwiO1xuXG4vKipcbiAqIE1pbmltYWwgRXhwcmVzcy1pc2ggcmVxdWVzdCBzaGFwZSB1c2VkIGJ5IHtAbGluayBleHRyYWN0TW9kZWxPdmVycmlkZX0uXG4gKiBLZWVwcyB0aGlzIG1vZHVsZSBpbmRlcGVuZGVudCBvZiBgZXhwcmVzc2Agc28gdGhlIGhlbHBlciBjYW4gYmVcbiAqIHJldXNlZCBmcm9tIG5vbi1FeHByZXNzIGFkYXB0ZXJzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIE1vZGVsT3ZlcnJpZGVSZXF1ZXN0IHtcbiAgaGVhZGVycz86IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkPjtcbiAgcXVlcnk/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZDtcbiAgYm9keT86IHVua25vd247XG59XG5cbi8qKlxuICogUHVsbCBhIG1vZGVsIG92ZXJyaWRlIG91dCBvZiBhIHNpbmdsZSBIVFRQIHJlcXVlc3QsIGNoZWNraW5nXG4gKiBzb3VyY2VzIGluIHByaW9yaXR5IG9yZGVyOlxuICpcbiAqICAgMS4gYFgtTWFzdHJhLU1vZGVsYCBoZWFkZXJcbiAqICAgMi4gYD9tb2RlbD1gIHF1ZXJ5IHN0cmluZyBwYXJhbWV0ZXJcbiAqICAgMy4gQm9keSBmaWVsZCAoYG1vZGVsYCBvciBgbW9kZWxJZGAsIGluIHRoYXQgb3JkZXIpXG4gKlxuICogUmV0dXJucyBgbnVsbGAgd2hlbiBub3RoaW5nIGlzIHNldCwgc28gY2FsbGVycyBjYW4gd3JhcCB3aXRoXG4gKiBgaWYgKG92ZXJyaWRlKSAuLi5gIHdpdGhvdXQganVnZ2xpbmcgZW1wdHkgc3RyaW5ncy4gQm9keSBpbnNwZWN0aW9uXG4gKiBpcyBsZW5pZW50IC0gYW55IHBsYWluIG9iamVjdCB3aXRoIG9uZSBvZiB0aGUgY29uZmlndXJlZCBrZXlzXG4gKiBjb3VudHMsIG1pcnJvcmluZyBob3cgQUkgU0RLIGNoYXQgY2xpZW50cyBwYXNzIGFyYml0cmFyeSBtZXRhZGF0YVxuICogYWxvbmdzaWRlIGBtZXNzYWdlc2AuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kZWxPdmVycmlkZShyZXE6IE1vZGVsT3ZlcnJpZGVSZXF1ZXN0KTogc3RyaW5nIHwgbnVsbCB7XG4gIGNvbnN0IGhlYWRlcnMgPSByZXEuaGVhZGVycztcbiAgaWYgKGhlYWRlcnMpIHtcbiAgICBjb25zdCBoZWFkZXJWYWwgPSBzdHJpbmcuZmlyc3ROb25FbXB0eShcbiAgICAgIGhlYWRlcnNbb3ZlcnJpZGUuTU9ERUxfT1ZFUlJJREVfSEVBREVSXSA/P1xuICAgICAgICBoZWFkZXJzW292ZXJyaWRlLk1PREVMX09WRVJSSURFX0hFQURFUi50b0xvd2VyQ2FzZSgpXSxcbiAgICApO1xuICAgIGlmIChoZWFkZXJWYWwpIHJldHVybiBoZWFkZXJWYWw7XG4gIH1cbiAgaWYgKHJlcS5xdWVyeSkge1xuICAgIGNvbnN0IHF1ZXJ5VmFsID0gc3RyaW5nLmZpcnN0Tm9uRW1wdHkocmVxLnF1ZXJ5W292ZXJyaWRlLk1PREVMX09WRVJSSURFX1FVRVJZXSk7XG4gICAgaWYgKHF1ZXJ5VmFsKSByZXR1cm4gcXVlcnlWYWw7XG4gIH1cbiAgaWYgKHJlcS5ib2R5ICYmIHR5cGVvZiByZXEuYm9keSA9PT0gXCJvYmplY3RcIikge1xuICAgIGNvbnN0IHJlY29yZCA9IHJlcS5ib2R5IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIGZvciAoY29uc3QgZmllbGQgb2Ygb3ZlcnJpZGUuTU9ERUxfT1ZFUlJJREVfQk9EWV9GSUVMRFMpIHtcbiAgICAgIGNvbnN0IGJvZHlWYWwgPSBzdHJpbmcuZmlyc3ROb25FbXB0eShyZWNvcmRbZmllbGRdKTtcbiAgICAgIGlmIChib2R5VmFsKSByZXR1cm4gYm9keVZhbDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogUmVhZCB0aGUgZnV6enktcmVzb2x1dGlvbiBjb25maWcga25vYnMgb2ZmIHRoZSBwbHVnaW4gY29uZmlnIHdpdGhcbiAqIGBAZGJ4LXRvb2xzL21vZGVsYCBkZWZhdWx0cyBhcHBsaWVkLiBLZXB0IGhlcmUgc28gYGJ1aWxkTW9kZWxgIGFuZFxuICogdGhlIGAvbW9kZWxzYCByb3V0ZSBhZ3JlZSBvbiB3aGF0IFwiZW5hYmxlZFwiIG1lYW5zLlxuICpcbiAqIGBmYWxsYmFja3NgIGlzIHRoZSBwcmlvcml0eS1vcmRlcmVkIGxpc3QgYHJlc29sdmVNb2RlbGAgd2Fsa3MgZmlyc3RcbiAqIHdoZW4gbm90aGluZyBleHBsaWNpdCBpcyBzZXQ7IGRlZmF1bHRzIHRvIGFuIGVtcHR5IGxpc3Qgc28gdGhlXG4gKiBnZW5lcmljIHJlc29sdmVyIGZhbGxzIHRocm91Z2ggdG8gdGhlIGxpdmUgY2F0YWxvZ3VlIGFuZCBpdHMgb3duXG4gKiBgRkFMTEJBQ0tfTU9ERUxfSURTYCBmbG9vci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVTZXJ2aW5nQ29uZmlnKGNvbmZpZzogTWFzdHJhUGx1Z2luQ29uZmlnKToge1xuICB0dGxNczogbnVtYmVyO1xuICB0aHJlc2hvbGQ6IG51bWJlcjtcbiAgZnV6enk6IGJvb2xlYW47XG4gIGFsbG93T3ZlcnJpZGU6IGJvb2xlYW47XG4gIGZhbGxiYWNrczogcmVhZG9ubHkgc3RyaW5nW107XG59IHtcbiAgcmV0dXJuIHtcbiAgICB0dGxNczogY29uZmlnLm1vZGVsQ2FjaGVUdGxNcyA/PyBub2RlU2VydmluZy5ERUZBVUxUX01PREVMX0NBQ0hFX1RUTF9NUyxcbiAgICB0aHJlc2hvbGQ6IGNvbmZpZy5tb2RlbEZ1enp5VGhyZXNob2xkID8/IG5vZGVTZXJ2aW5nLkRFRkFVTFRfRlVaWllfVEhSRVNIT0xELFxuICAgIGZ1enp5OiBjb25maWcubW9kZWxGdXp6eU1hdGNoICE9PSBmYWxzZSxcbiAgICBhbGxvd092ZXJyaWRlOiBjb25maWcubW9kZWxPdmVycmlkZSAhPT0gZmFsc2UsXG4gICAgZmFsbGJhY2tzOiBjb25maWcuZGVmYXVsdE1vZGVsRmFsbGJhY2tzID8/IFtdLFxuICB9O1xufVxuIl19
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `client.statementExecution.getStatement` with the shape and
|
|
5
|
+
* size handling the plugin's tools and the `/embed/data/:id` route
|
|
6
|
+
* both need: a low-level fetch that returns a raw
|
|
7
|
+
* `{columns, rows, rowCount}` shape and coerces numeric strings to
|
|
8
|
+
* numbers so downstream charts and aggregations don't have to, plus a
|
|
9
|
+
* hard row cap callers clamp `limit` to so a runaway result set can't
|
|
10
|
+
* hose a response. Upstream 404s are detected via
|
|
11
|
+
* `error.errorContext(err).notFound` at the call sites.
|
|
12
|
+
*
|
|
13
|
+
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
14
|
+
* scoped and lives in the Statement Execution API regardless of
|
|
15
|
+
* which producer (Genie, a tool, a notebook, etc.) submitted the
|
|
16
|
+
* query. Co-located here so consumers can fetch / cap / handle
|
|
17
|
+
* 404s without reaching into the Genie tool module.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { WorkspaceClient } from "@databricks/sdk-experimental";
|
|
22
|
+
import type { GenieDatasetData } from "@dbx-tools/shared-mastra";
|
|
23
|
+
/**
|
|
24
|
+
* Hard server-side cap on rows returned by the
|
|
25
|
+
* `/embed/data/:id` route. Sized to keep responses small
|
|
26
|
+
* enough for inline tables to render snappily; the route surfaces
|
|
27
|
+
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
28
|
+
* this so end users know they're seeing a sample.
|
|
29
|
+
*/
|
|
30
|
+
export declare const STATEMENT_ROW_CAP = 500;
|
|
31
|
+
/**
|
|
32
|
+
* Fetch a single statement's rows via the Statement Execution API
|
|
33
|
+
* and reshape into the shared {@link GenieDatasetData} shape
|
|
34
|
+
* (column array + row records).
|
|
35
|
+
*
|
|
36
|
+
* Optional `limit` slices the returned `rows` client-side so the
|
|
37
|
+
* agent can scan a small sample without paging the full result
|
|
38
|
+
* set into context. `rowCount` always reflects the upstream total
|
|
39
|
+
* so callers know when the slice truncated.
|
|
40
|
+
*
|
|
41
|
+
* Exported because every consumer in the plugin (the
|
|
42
|
+
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
43
|
+
* the `/embed/data/:id` route) needs the exact same
|
|
44
|
+
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
45
|
+
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
46
|
+
* the same `statement_id`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function fetchStatementData(client: WorkspaceClient, statementId: string, options?: {
|
|
49
|
+
limit?: number;
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
}): Promise<GenieDatasetData>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `client.statementExecution.getStatement` with the shape and
|
|
5
|
+
* size handling the plugin's tools and the `/embed/data/:id` route
|
|
6
|
+
* both need: a low-level fetch that returns a raw
|
|
7
|
+
* `{columns, rows, rowCount}` shape and coerces numeric strings to
|
|
8
|
+
* numbers so downstream charts and aggregations don't have to, plus a
|
|
9
|
+
* hard row cap callers clamp `limit` to so a runaway result set can't
|
|
10
|
+
* hose a response. Upstream 404s are detected via
|
|
11
|
+
* `error.errorContext(err).notFound` at the call sites.
|
|
12
|
+
*
|
|
13
|
+
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
14
|
+
* scoped and lives in the Statement Execution API regardless of
|
|
15
|
+
* which producer (Genie, a tool, a notebook, etc.) submitted the
|
|
16
|
+
* query. Co-located here so consumers can fetch / cap / handle
|
|
17
|
+
* 404s without reaching into the Genie tool module.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { databricks } from "@dbx-tools/appkit";
|
|
22
|
+
/**
|
|
23
|
+
* Hard server-side cap on rows returned by the
|
|
24
|
+
* `/embed/data/:id` route. Sized to keep responses small
|
|
25
|
+
* enough for inline tables to render snappily; the route surfaces
|
|
26
|
+
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
27
|
+
* this so end users know they're seeing a sample.
|
|
28
|
+
*/
|
|
29
|
+
export const STATEMENT_ROW_CAP = 500;
|
|
30
|
+
/**
|
|
31
|
+
* Best-effort numeric coercion for the Statement Execution API's
|
|
32
|
+
* all-strings cells. Leaves non-numeric strings (and explicit
|
|
33
|
+
* `null`s) intact; everything else flows through `Number`.
|
|
34
|
+
*/
|
|
35
|
+
function coerceCell(cell) {
|
|
36
|
+
if (cell === null)
|
|
37
|
+
return null;
|
|
38
|
+
if (/^-?\d+(\.\d+)?$/.test(cell)) {
|
|
39
|
+
const n = Number(cell);
|
|
40
|
+
if (Number.isFinite(n))
|
|
41
|
+
return n;
|
|
42
|
+
}
|
|
43
|
+
return cell;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fetch a single statement's rows via the Statement Execution API
|
|
47
|
+
* and reshape into the shared {@link GenieDatasetData} shape
|
|
48
|
+
* (column array + row records).
|
|
49
|
+
*
|
|
50
|
+
* Optional `limit` slices the returned `rows` client-side so the
|
|
51
|
+
* agent can scan a small sample without paging the full result
|
|
52
|
+
* set into context. `rowCount` always reflects the upstream total
|
|
53
|
+
* so callers know when the slice truncated.
|
|
54
|
+
*
|
|
55
|
+
* Exported because every consumer in the plugin (the
|
|
56
|
+
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
57
|
+
* the `/embed/data/:id` route) needs the exact same
|
|
58
|
+
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
59
|
+
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
60
|
+
* the same `statement_id`.
|
|
61
|
+
*/
|
|
62
|
+
export async function fetchStatementData(client, statementId, options) {
|
|
63
|
+
const ctx = options?.signal ? databricks.toContext(options.signal) : undefined;
|
|
64
|
+
const r = await client.statementExecution.getStatement({ statement_id: statementId }, ctx);
|
|
65
|
+
const columns = (r.manifest?.schema?.columns ?? []).map((c) => c.name ?? "");
|
|
66
|
+
const dataArray = (r.result?.data_array ?? []);
|
|
67
|
+
const sliced = options?.limit !== undefined && options.limit >= 0
|
|
68
|
+
? dataArray.slice(0, options.limit)
|
|
69
|
+
: dataArray;
|
|
70
|
+
const rows = sliced.map((row) => {
|
|
71
|
+
const obj = {};
|
|
72
|
+
columns.forEach((col, i) => {
|
|
73
|
+
obj[col] = coerceCell(row[i] ?? null);
|
|
74
|
+
});
|
|
75
|
+
return obj;
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
columns,
|
|
79
|
+
rows,
|
|
80
|
+
rowCount: r.manifest?.total_row_count ?? dataArray.length,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGVtZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0YXRlbWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUdILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUcvQzs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxHQUFHLENBQUM7QUFFckM7Ozs7R0FJRztBQUNILFNBQVMsVUFBVSxDQUFDLElBQW1CO0lBQ3JDLElBQUksSUFBSSxLQUFLLElBQUk7UUFBRSxPQUFPLElBQUksQ0FBQztJQUMvQixJQUFJLGlCQUFpQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ2pDLE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QixJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1lBQUUsT0FBTyxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxrQkFBa0IsQ0FDdEMsTUFBdUIsRUFDdkIsV0FBbUIsRUFDbkIsT0FBa0Q7SUFFbEQsTUFBTSxHQUFHLEdBQUcsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUMvRSxNQUFNLENBQUMsR0FBRyxNQUFNLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLENBQUMsRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDM0YsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxPQUFPLElBQUksRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzdFLE1BQU0sU0FBUyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxVQUFVLElBQUksRUFBRSxDQUFnQyxDQUFDO0lBQzlFLE1BQU0sTUFBTSxHQUNWLE9BQU8sRUFBRSxLQUFLLEtBQUssU0FBUyxJQUFJLE9BQU8sQ0FBQyxLQUFLLElBQUksQ0FBQztRQUNoRCxDQUFDLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUNuQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQ2hCLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtRQUM5QixNQUFNLEdBQUcsR0FBNEIsRUFBRSxDQUFDO1FBQ3hDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDekIsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUM7UUFDeEMsQ0FBQyxDQUFDLENBQUM7UUFDSCxPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTztRQUNMLE9BQU87UUFDUCxJQUFJO1FBQ0osUUFBUSxFQUFFLENBQUMsQ0FBQyxRQUFRLEVBQUUsZUFBZSxJQUFJLFNBQVMsQ0FBQyxNQUFNO0tBQzFELENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEYXRhYnJpY2tzIFN0YXRlbWVudCBFeGVjdXRpb24gaGVscGVycyBmb3IgdGhlIE1hc3RyYSBwbHVnaW4uXG4gKlxuICogV3JhcHMgYGNsaWVudC5zdGF0ZW1lbnRFeGVjdXRpb24uZ2V0U3RhdGVtZW50YCB3aXRoIHRoZSBzaGFwZSBhbmRcbiAqIHNpemUgaGFuZGxpbmcgdGhlIHBsdWdpbidzIHRvb2xzIGFuZCB0aGUgYC9lbWJlZC9kYXRhLzppZGAgcm91dGVcbiAqIGJvdGggbmVlZDogYSBsb3ctbGV2ZWwgZmV0Y2ggdGhhdCByZXR1cm5zIGEgcmF3XG4gKiBge2NvbHVtbnMsIHJvd3MsIHJvd0NvdW50fWAgc2hhcGUgYW5kIGNvZXJjZXMgbnVtZXJpYyBzdHJpbmdzIHRvXG4gKiBudW1iZXJzIHNvIGRvd25zdHJlYW0gY2hhcnRzIGFuZCBhZ2dyZWdhdGlvbnMgZG9uJ3QgaGF2ZSB0bywgcGx1cyBhXG4gKiBoYXJkIHJvdyBjYXAgY2FsbGVycyBjbGFtcCBgbGltaXRgIHRvIHNvIGEgcnVuYXdheSByZXN1bHQgc2V0IGNhbid0XG4gKiBob3NlIGEgcmVzcG9uc2UuIFVwc3RyZWFtIDQwNHMgYXJlIGRldGVjdGVkIHZpYVxuICogYGVycm9yLmVycm9yQ29udGV4dChlcnIpLm5vdEZvdW5kYCBhdCB0aGUgY2FsbCBzaXRlcy5cbiAqXG4gKiBOb3QgR2VuaWUtc3BlY2lmaWM6IGEgRGF0YWJyaWNrcyBgc3RhdGVtZW50X2lkYCBpcyB3b3Jrc3BhY2VcbiAqIHNjb3BlZCBhbmQgbGl2ZXMgaW4gdGhlIFN0YXRlbWVudCBFeGVjdXRpb24gQVBJIHJlZ2FyZGxlc3Mgb2ZcbiAqIHdoaWNoIHByb2R1Y2VyIChHZW5pZSwgYSB0b29sLCBhIG5vdGVib29rLCBldGMuKSBzdWJtaXR0ZWQgdGhlXG4gKiBxdWVyeS4gQ28tbG9jYXRlZCBoZXJlIHNvIGNvbnN1bWVycyBjYW4gZmV0Y2ggLyBjYXAgLyBoYW5kbGVcbiAqIDQwNHMgd2l0aG91dCByZWFjaGluZyBpbnRvIHRoZSBHZW5pZSB0b29sIG1vZHVsZS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgV29ya3NwYWNlQ2xpZW50IH0gZnJvbSBcIkBkYXRhYnJpY2tzL3Nkay1leHBlcmltZW50YWxcIjtcbmltcG9ydCB7IGRhdGFicmlja3MgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB0eXBlIHsgR2VuaWVEYXRhc2V0RGF0YSB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1tYXN0cmFcIjtcblxuLyoqXG4gKiBIYXJkIHNlcnZlci1zaWRlIGNhcCBvbiByb3dzIHJldHVybmVkIGJ5IHRoZVxuICogYC9lbWJlZC9kYXRhLzppZGAgcm91dGUuIFNpemVkIHRvIGtlZXAgcmVzcG9uc2VzIHNtYWxsXG4gKiBlbm91Z2ggZm9yIGlubGluZSB0YWJsZXMgdG8gcmVuZGVyIHNuYXBwaWx5OyB0aGUgcm91dGUgc3VyZmFjZXNcbiAqIGEgYHRydW5jYXRlZGAgZmxhZyB3aGVuZXZlciB0aGUgdXBzdHJlYW0gYHJvd0NvdW50YCBleGNlZWRzXG4gKiB0aGlzIHNvIGVuZCB1c2VycyBrbm93IHRoZXkncmUgc2VlaW5nIGEgc2FtcGxlLlxuICovXG5leHBvcnQgY29uc3QgU1RBVEVNRU5UX1JPV19DQVAgPSA1MDA7XG5cbi8qKlxuICogQmVzdC1lZmZvcnQgbnVtZXJpYyBjb2VyY2lvbiBmb3IgdGhlIFN0YXRlbWVudCBFeGVjdXRpb24gQVBJJ3NcbiAqIGFsbC1zdHJpbmdzIGNlbGxzLiBMZWF2ZXMgbm9uLW51bWVyaWMgc3RyaW5ncyAoYW5kIGV4cGxpY2l0XG4gKiBgbnVsbGBzKSBpbnRhY3Q7IGV2ZXJ5dGhpbmcgZWxzZSBmbG93cyB0aHJvdWdoIGBOdW1iZXJgLlxuICovXG5mdW5jdGlvbiBjb2VyY2VDZWxsKGNlbGw6IHN0cmluZyB8IG51bGwpOiB1bmtub3duIHtcbiAgaWYgKGNlbGwgPT09IG51bGwpIHJldHVybiBudWxsO1xuICBpZiAoL14tP1xcZCsoXFwuXFxkKyk/JC8udGVzdChjZWxsKSkge1xuICAgIGNvbnN0IG4gPSBOdW1iZXIoY2VsbCk7XG4gICAgaWYgKE51bWJlci5pc0Zpbml0ZShuKSkgcmV0dXJuIG47XG4gIH1cbiAgcmV0dXJuIGNlbGw7XG59XG5cbi8qKlxuICogRmV0Y2ggYSBzaW5nbGUgc3RhdGVtZW50J3Mgcm93cyB2aWEgdGhlIFN0YXRlbWVudCBFeGVjdXRpb24gQVBJXG4gKiBhbmQgcmVzaGFwZSBpbnRvIHRoZSBzaGFyZWQge0BsaW5rIEdlbmllRGF0YXNldERhdGF9IHNoYXBlXG4gKiAoY29sdW1uIGFycmF5ICsgcm93IHJlY29yZHMpLlxuICpcbiAqIE9wdGlvbmFsIGBsaW1pdGAgc2xpY2VzIHRoZSByZXR1cm5lZCBgcm93c2AgY2xpZW50LXNpZGUgc28gdGhlXG4gKiBhZ2VudCBjYW4gc2NhbiBhIHNtYWxsIHNhbXBsZSB3aXRob3V0IHBhZ2luZyB0aGUgZnVsbCByZXN1bHRcbiAqIHNldCBpbnRvIGNvbnRleHQuIGByb3dDb3VudGAgYWx3YXlzIHJlZmxlY3RzIHRoZSB1cHN0cmVhbSB0b3RhbFxuICogc28gY2FsbGVycyBrbm93IHdoZW4gdGhlIHNsaWNlIHRydW5jYXRlZC5cbiAqXG4gKiBFeHBvcnRlZCBiZWNhdXNlIGV2ZXJ5IGNvbnN1bWVyIGluIHRoZSBwbHVnaW4gKHRoZVxuICogYGdldF9zdGF0ZW1lbnRgIHRvb2wsIHRoZSBgcHJlcGFyZV9jaGFydGAgZGF0YXNldCByZXNvbHZlciwgYW5kXG4gKiB0aGUgYC9lbWJlZC9kYXRhLzppZGAgcm91dGUpIG5lZWRzIHRoZSBleGFjdCBzYW1lXG4gKiBmZXRjaCArIGNvZXJjaW9uIHBpcGVsaW5lIHNvIExMTS1zaWRlIGBnZXRfc3RhdGVtZW50YCBvdXRwdXRcbiAqIGFuZCBVSS1zaWRlIGBbZGF0YTo8aWQ+XWAgcmVuZGVyaW5nIHN0YXkgc2hhcGUtaWRlbnRpY2FsIGZvclxuICogdGhlIHNhbWUgYHN0YXRlbWVudF9pZGAuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaFN0YXRlbWVudERhdGEoXG4gIGNsaWVudDogV29ya3NwYWNlQ2xpZW50LFxuICBzdGF0ZW1lbnRJZDogc3RyaW5nLFxuICBvcHRpb25zPzogeyBsaW1pdD86IG51bWJlcjsgc2lnbmFsPzogQWJvcnRTaWduYWwgfSxcbik6IFByb21pc2U8R2VuaWVEYXRhc2V0RGF0YT4ge1xuICBjb25zdCBjdHggPSBvcHRpb25zPy5zaWduYWwgPyBkYXRhYnJpY2tzLnRvQ29udGV4dChvcHRpb25zLnNpZ25hbCkgOiB1bmRlZmluZWQ7XG4gIGNvbnN0IHIgPSBhd2FpdCBjbGllbnQuc3RhdGVtZW50RXhlY3V0aW9uLmdldFN0YXRlbWVudCh7IHN0YXRlbWVudF9pZDogc3RhdGVtZW50SWQgfSwgY3R4KTtcbiAgY29uc3QgY29sdW1ucyA9IChyLm1hbmlmZXN0Py5zY2hlbWE/LmNvbHVtbnMgPz8gW10pLm1hcCgoYykgPT4gYy5uYW1lID8/IFwiXCIpO1xuICBjb25zdCBkYXRhQXJyYXkgPSAoci5yZXN1bHQ/LmRhdGFfYXJyYXkgPz8gW10pIGFzIEFycmF5PEFycmF5PHN0cmluZyB8IG51bGw+PjtcbiAgY29uc3Qgc2xpY2VkID1cbiAgICBvcHRpb25zPy5saW1pdCAhPT0gdW5kZWZpbmVkICYmIG9wdGlvbnMubGltaXQgPj0gMFxuICAgICAgPyBkYXRhQXJyYXkuc2xpY2UoMCwgb3B0aW9ucy5saW1pdClcbiAgICAgIDogZGF0YUFycmF5O1xuICBjb25zdCByb3dzID0gc2xpY2VkLm1hcCgocm93KSA9PiB7XG4gICAgY29uc3Qgb2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICAgIGNvbHVtbnMuZm9yRWFjaCgoY29sLCBpKSA9PiB7XG4gICAgICBvYmpbY29sXSA9IGNvZXJjZUNlbGwocm93W2ldID8/IG51bGwpO1xuICAgIH0pO1xuICAgIHJldHVybiBvYmo7XG4gIH0pO1xuICByZXR1cm4ge1xuICAgIGNvbHVtbnMsXG4gICAgcm93cyxcbiAgICByb3dDb3VudDogci5tYW5pZmVzdD8udG90YWxfcm93X2NvdW50ID8/IGRhdGFBcnJheS5sZW5ndGgsXG4gIH07XG59XG4iXX0=
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a Postgres-safe schema name for per-agent Mastra storage.
|
|
3
|
+
*
|
|
4
|
+
* Agent ids are often kebab-case route segments (`data-mesh-book-assistant`)
|
|
5
|
+
* but {@link PostgresStore} validates `schemaName` with Mastra's
|
|
6
|
+
* `parseSqlIdentifier` (letter/underscore start, `[A-Za-z0-9_]` only, max 63).
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Default Lakebase schema for one agent's thread/message store:
|
|
12
|
+
* `mastra_<sanitized-agent-id>`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function agentStorageSchemaName(agentId: string): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a Postgres-safe schema name for per-agent Mastra storage.
|
|
3
|
+
*
|
|
4
|
+
* Agent ids are often kebab-case route segments (`data-mesh-book-assistant`)
|
|
5
|
+
* but {@link PostgresStore} validates `schemaName` with Mastra's
|
|
6
|
+
* `parseSqlIdentifier` (letter/underscore start, `[A-Za-z0-9_]` only, max 63).
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { string } from "@dbx-tools/shared-core";
|
|
11
|
+
const SCHEMA_PREFIX = "mastra_";
|
|
12
|
+
const MAX_PG_IDENTIFIER_LEN = 63;
|
|
13
|
+
/**
|
|
14
|
+
* Default Lakebase schema for one agent's thread/message store:
|
|
15
|
+
* `mastra_<sanitized-agent-id>`.
|
|
16
|
+
*/
|
|
17
|
+
export function agentStorageSchemaName(agentId) {
|
|
18
|
+
const maxSlugLen = MAX_PG_IDENTIFIER_LEN - SCHEMA_PREFIX.length;
|
|
19
|
+
const slug = string.toIdentifierWithOptions({
|
|
20
|
+
delimiter: "_",
|
|
21
|
+
maxLength: maxSlugLen,
|
|
22
|
+
truncateStrategy: "hash",
|
|
23
|
+
truncateHashLength: 6,
|
|
24
|
+
}, agentId);
|
|
25
|
+
const body = slug ||
|
|
26
|
+
string.toIdentifierWithOptions({
|
|
27
|
+
delimiter: "_",
|
|
28
|
+
maxLength: maxSlugLen,
|
|
29
|
+
truncateStrategy: "hash",
|
|
30
|
+
truncateHashLength: 6,
|
|
31
|
+
}, "agent", agentId);
|
|
32
|
+
return `${SCHEMA_PREFIX}${body}`;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZS1zY2hlbWEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmFnZS1zY2hlbWEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFaEQsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDO0FBQ2hDLE1BQU0scUJBQXFCLEdBQUcsRUFBRSxDQUFDO0FBRWpDOzs7R0FHRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxPQUFlO0lBQ3BELE1BQU0sVUFBVSxHQUFHLHFCQUFxQixHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUM7SUFDaEUsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLHVCQUF1QixDQUN6QztRQUNFLFNBQVMsRUFBRSxHQUFHO1FBQ2QsU0FBUyxFQUFFLFVBQVU7UUFDckIsZ0JBQWdCLEVBQUUsTUFBTTtRQUN4QixrQkFBa0IsRUFBRSxDQUFDO0tBQ3RCLEVBQ0QsT0FBTyxDQUNSLENBQUM7SUFDRixNQUFNLElBQUksR0FDUixJQUFJO1FBQ0osTUFBTSxDQUFDLHVCQUF1QixDQUM1QjtZQUNFLFNBQVMsRUFBRSxHQUFHO1lBQ2QsU0FBUyxFQUFFLFVBQVU7WUFDckIsZ0JBQWdCLEVBQUUsTUFBTTtZQUN4QixrQkFBa0IsRUFBRSxDQUFDO1NBQ3RCLEVBQ0QsT0FBTyxFQUNQLE9BQU8sQ0FDUixDQUFDO0lBQ0osT0FBTyxHQUFHLGFBQWEsR0FBRyxJQUFJLEVBQUUsQ0FBQztBQUNuQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEZXJpdmUgYSBQb3N0Z3Jlcy1zYWZlIHNjaGVtYSBuYW1lIGZvciBwZXItYWdlbnQgTWFzdHJhIHN0b3JhZ2UuXG4gKlxuICogQWdlbnQgaWRzIGFyZSBvZnRlbiBrZWJhYi1jYXNlIHJvdXRlIHNlZ21lbnRzIChgZGF0YS1tZXNoLWJvb2stYXNzaXN0YW50YClcbiAqIGJ1dCB7QGxpbmsgUG9zdGdyZXNTdG9yZX0gdmFsaWRhdGVzIGBzY2hlbWFOYW1lYCB3aXRoIE1hc3RyYSdzXG4gKiBgcGFyc2VTcWxJZGVudGlmaWVyYCAobGV0dGVyL3VuZGVyc2NvcmUgc3RhcnQsIGBbQS1aYS16MC05X11gIG9ubHksIG1heCA2MykuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbmNvbnN0IFNDSEVNQV9QUkVGSVggPSBcIm1hc3RyYV9cIjtcbmNvbnN0IE1BWF9QR19JREVOVElGSUVSX0xFTiA9IDYzO1xuXG4vKipcbiAqIERlZmF1bHQgTGFrZWJhc2Ugc2NoZW1hIGZvciBvbmUgYWdlbnQncyB0aHJlYWQvbWVzc2FnZSBzdG9yZTpcbiAqIGBtYXN0cmFfPHNhbml0aXplZC1hZ2VudC1pZD5gLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYWdlbnRTdG9yYWdlU2NoZW1hTmFtZShhZ2VudElkOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBtYXhTbHVnTGVuID0gTUFYX1BHX0lERU5USUZJRVJfTEVOIC0gU0NIRU1BX1BSRUZJWC5sZW5ndGg7XG4gIGNvbnN0IHNsdWcgPSBzdHJpbmcudG9JZGVudGlmaWVyV2l0aE9wdGlvbnMoXG4gICAge1xuICAgICAgZGVsaW1pdGVyOiBcIl9cIixcbiAgICAgIG1heExlbmd0aDogbWF4U2x1Z0xlbixcbiAgICAgIHRydW5jYXRlU3RyYXRlZ3k6IFwiaGFzaFwiLFxuICAgICAgdHJ1bmNhdGVIYXNoTGVuZ3RoOiA2LFxuICAgIH0sXG4gICAgYWdlbnRJZCxcbiAgKTtcbiAgY29uc3QgYm9keSA9XG4gICAgc2x1ZyB8fFxuICAgIHN0cmluZy50b0lkZW50aWZpZXJXaXRoT3B0aW9ucyhcbiAgICAgIHtcbiAgICAgICAgZGVsaW1pdGVyOiBcIl9cIixcbiAgICAgICAgbWF4TGVuZ3RoOiBtYXhTbHVnTGVuLFxuICAgICAgICB0cnVuY2F0ZVN0cmF0ZWd5OiBcImhhc2hcIixcbiAgICAgICAgdHJ1bmNhdGVIYXNoTGVuZ3RoOiA2LFxuICAgICAgfSxcbiAgICAgIFwiYWdlbnRcIixcbiAgICAgIGFnZW50SWQsXG4gICAgKTtcbiAgcmV0dXJuIGAke1NDSEVNQV9QUkVGSVh9JHtib2R5fWA7XG59XG4iXX0=
|